@[TOC]
以下圖所示,這是一棵普通的樹,該如何存儲呢?一般,存儲具備普通樹結構數據的方法有 3 種:
雙親表示法;
孩子表示法;
孩子兄弟表示法;
圖1node
雙親表示法採用順序表(也就是數組)存儲普通樹,其實現的核心思想是:順序存儲各個節點的同時,給各節點附加一個記錄其父節點位置的變量。
注意,根節點沒有父節點(父節點又稱爲雙親節點),所以根節點記錄父節點位置的變量一般置爲 -1。
圖2
雙親表示法存儲普通樹代碼數組
/* * @Description: 樹的雙親表示法 * @Version: V1.0 * @Autor: Carlos * @Date: 2020-05-21 14:41:32 * @LastEditors: Carlos * @LastEditTime: 2020-06-01 22:12:34 */ #include<stdio.h> #include<stdlib.h> //宏定義樹中結點的最大數量 #define MAX_SIZE 20 //宏定義樹結構中數據類型 typedef char ElemType; //結點結構 typedef struct Snode { //樹中結點的數據類型 ElemType data; //結點的父結點在數組中的位置下標 int parent; }PNode; //樹結構 typedef struct { //存放樹中全部結點 PNode tnode[MAX_SIZE]; //結點個數 int n; }PTree; /** * @Description: 節點初始化 * @Param: PTree tree 結構體變量 * @Return: PTree 結構體地址 * @Author: Carlos */ PTree InitPNode(PTree tree) { int i,j; char ch; printf("請輸入節點個數:\n"); scanf("%d",&(tree.n)); printf("請輸入結點的值其雙親位於數組中的位置下標:\n"); for(i=0; i<tree.n; i++) { fflush(stdin); scanf("%c %d",&ch,&j); tree.tnode[i].data = ch; tree.tnode[i].parent = j; } return tree; } /** * @Description: 查找樹中指定節點 * @Param: PTree tree 結構體變量 * @Return: 無 * @Author: Carlos */ void FindParent(PTree tree) { char a; int isfind = 0; printf("請輸入要查詢的結點值:\n"); fflush(stdin); scanf("%c",&a); for(int i =0;i<tree.n;i++){ if(tree.tnode[i].data == a){ isfind=1; //找到父節點的下標數值 int ad=tree.tnode[i].parent; printf("%c的父節點爲 %c,存儲位置下標爲 %d",a,tree.tnode[ad].data,ad); break; } } if(isfind == 0){ printf("樹中無此節點"); } } int main() { PTree tree; tree = InitPNode(tree); FindParent(tree); return 0; }
孩子表示法存儲普通樹採用的是 "順序表+鏈表" 的組合結構,其存儲過程是:從樹的根節點開始,使用順序表依次存儲樹中各個節點,須要注意的是,與雙親表示法不一樣,孩子表示法會給各個節點配備一個鏈表,用於存儲各節點的孩子節點位於順序表中的位置。
若是節點沒有孩子節點(葉子節點),則該節點的鏈表爲空鏈表。
例如,使用孩子表示法存儲左圖中的普通樹,則最終存儲狀態如右圖所示:
圖3spa
/* * @Description: 樹的孩子表示法。三部分構成,鏈表,節點,樹 * @Version: * @Autor: Carlos * @Date: 2020-05-21 14:59:47 * @LastEditors: Carlos * @LastEditTime: 2020-06-01 22:47:38 */ #include<stdio.h> #include<stdlib.h> #define MAX_SIZE 20 #define TElemType char typedef struct CTNode{ //鏈表中每一個結點存儲的不是數據自己,而是數據在數組中存儲的位置下標!! int child; struct CTNode * next; }ChildPtr; typedef struct { //結點的數據類型 TElemType data; //孩子鏈表的頭指針 ChildPtr* firstchild; }CTBox; typedef struct{ //存儲結點的數組 CTBox nodes[MAX_SIZE]; //結點數量和樹根的位置 int n,r; }CTree; /** * @Description: 孩子表示法存儲普通樹 * @Param: CTree tree 樹的結構體變量 * @Return: CTree tree 結構體變量 * @Author: Carlos */ CTree InitTree(CTree tree){ printf("輸入節點數量:\n"); scanf("%d",&(tree.n)); for(int i=0;i<tree.n;i++){ printf("輸入第 %d 個節點的值:\n",i+1); fflush(stdin); scanf("%c",&(tree.nodes[i].data)); tree.nodes[i].firstchild=(ChildPtr*)malloc(sizeof(ChildPtr)); tree.nodes[i].firstchild->next=NULL; printf("輸入節點 %c 的孩子節點數量:\n",tree.nodes[i].data); int Num; scanf("%d",&Num); if(Num!=0){ //帶頭結點的鏈表 ChildPtr * p = tree.nodes[i].firstchild; for(int j = 0 ;j<Num;j++){ ChildPtr * newEle=(ChildPtr*)malloc(sizeof(ChildPtr)); newEle->next=NULL; printf("輸入第 %d 個孩子節點在順序表中的位置",j+1); scanf("%d",&(newEle->child)); p->next= newEle; p=p->next; } } } return tree; } /** * @Description:查找節點 * @Param: CTree tree 樹的結構體,char a 要查找的節點 * @Return: 無 * @Author: Carlos */ void FindKids(CTree tree,char a){ int hasKids=0; for(int i=0;i<tree.n;i++){ if(tree.nodes[i].data==a){ ChildPtr * p=tree.nodes[i].firstchild->next; while(p){ hasKids = 1; //打印全部孩子節點 p->child 孩子節點在數組中的位置 printf("%c ",tree.nodes[p->child].data); p=p->next; } break; } } if(hasKids==0){ printf("此節點爲葉子節點"); } } int main() { CTree tree; tree = InitTree(tree); //默認數根節點位於數組notes[0]處 tree.r=0; printf("找出節點 F 的全部孩子節點:"); FindKids(tree,'F'); return 0; }
樹結構中,位於同一層的節點之間互爲兄弟節點。例如,圖1中的普通樹中,節點 A、B 和 C 互爲兄弟節點,而節點 D、E 和 F 也互爲兄弟節點。
孩子兄弟表示法,採用的是鏈式存儲結構,其存儲樹的實現思想是:從樹的根節點開始,依次用鏈表存儲各個節點的孩子節點和兄弟節點。
所以,該鏈表中的節點應包含如下 3 部份內容:
節點的值;
指向孩子節點的指針;
指向兄弟節點的指針;
圖4
用 C 語言代碼表示節點結構爲:指針
#define ElemType char typedef struct CSNode{ ElemType data; struct CSNode * firstchild,*nextsibling; }CSNode,*CSTree;
以圖1爲例,使用孩子兄弟表示法進行存儲的結果以下圖所示:
圖5
由圖5能夠看到,節點 R 無兄弟節點,其孩子節點是 A;節點 A 的兄弟節點分別是 B 和 C,其孩子節點爲 D,依次類推。
實現上圖中的 C 語言實現代碼也很簡單,根據圖中鏈表的結構便可輕鬆完成鏈表的建立和使用,所以再也不給出具體代碼。
接下來觀察圖 1 和圖 5。圖 1 爲原普通樹,圖5 是由圖 1 通過孩子兄弟表示法轉化而來的一棵樹,確切地說,圖5是一棵二叉樹。所以能夠得出這樣一個結論,即經過孩子兄弟表示法,任意一棵普通樹均可以相應轉化爲一棵二叉樹,換句話說,任意一棵普通樹都有惟一的一棵二叉樹於其對應。
所以,孩子兄弟表示法能夠做爲將普通樹轉化爲二叉樹的最有效方法,一般又被稱爲"二叉樹表示法"或"二叉鏈表表示法"。code
如遇到排版錯亂的問題,能夠經過如下連接訪問個人CSDN。blog
**CSDN:[CSDN搜索「嵌入式與Linux那些事」]圖片