存儲結構表示方式:雙親表示法,孩子表示法,孩子兄弟表示法。
一、雙親表示法:
以雙親作為索引的關鍵詞的一種存儲方式
除根結點外,每個結點只有一個雙親,所以選擇順序存儲占主要
以一組連續空間存儲樹的結點,同時在每個結點中,附設一個指示其雙親結點位置的指針域
1、結點結構:
2、結點結構定義:
/*樹的雙親表示法結點結構定義*/ #define MAX_TREE_SIZE 100 typedef int TElemType; typedef struct PTNode //結點結構 { TElemType data; //結點數據 int parent; //雙親位置 }PTNode; typedef struct //樹結構 { PTNode nodes[MAX_TREE_SIZE]; //結點數組 int r, n; //r是根位置,n是結點數 }PTree;
優點:parent指針域指向數組下標,所以找雙親結點的時間復雜度為O(1),向上一直找到根節點也快
缺點:由上向下找就十分慢,若要找結點的孩子或者兄弟,要遍歷整個樹
3、改進一:方便獲取孩子結點
在雙親結點基礎上加入孩子結點位置,由於可能一個結點有多個子樹,所以我們要根據樹的度來設置添加幾個孩子結點的元素
樹的度為3,所以我們在結點結構設置上添加3個指針域,指向孩子結點,若是孩子為空則位置為-1。
#define MAX_TREE_SIZE 100 typedef int TElemType; typedef struct PTNode //結點結構 { TElemType data; //結點數據 int parent; //雙親位置 int child1; //孩子1 int child2; //孩子2 int child3; //孩子3 }PTNode; typedef struct //樹結構 { PTNode nodes[MAX_TREE_SIZE]; //結點數組 int r, n; //r是根位置,n是結點數 }PTree;
缺點:這樣消耗了大量的空間,是不必要的,我們盡可能使用較小的空間,所以我們一般只添加一個長子域,可以獲取到有0個或1個孩子結點,注意:長子域是最左邊孩子的域。
#define MAX_TREE_SIZE 100 typedef int TElemType; typedef struct PTNode //結點結構 { TElemType data; //結點數據 int parent; //雙親位置 int firstchild; //長子域 }PTNode; typedef struct //樹結構 { PTNode nodes[MAX_TREE_SIZE]; //結點數組 int r, n; //r是根位置,n是結點數 }PTree;
4.改進二:方便獲取各兄弟之間的關系
我們只需要增加一個有兄弟域,即可依次獲取所有的兄弟結點
#define MAX_TREE_SIZE 100 typedef int TElemType; typedef struct PTNode //結點結構 { TElemType data; //結點數據 int parent; //雙親位置 int rightsib; //右兄弟結點 }PTNode; typedef struct //樹結構 { PTNode nodes[MAX_TREE_SIZE]; //結點數組 int r, n; //r是根位置,n是結點數 }PTree;
存儲結構的設計是一個十分靈活的過程。一個存儲結構設計是否合理,取決於基於該存儲結構的運算是否合適,方便,時間復雜度好不好等。
例如若是我們既關注孩子又關注兄弟,而且對時間遍歷要求高,那么我們可以擴展上面結構含有雙親域,長子域,右兄弟域
二、孩子表示法
由於每個結點可有多個子樹(無法確定子樹個數),可以考慮使用多重鏈表來實現。
根據樹的度來設置孩子域的個數,例如本例中度為3,設置3個孩子域
#define MAX_TREE_SIZE 100 typedef int TElemType; typedef struct PTNode //結點結構 { TElemType data; //結點數據 int child1; //孩子1 int child2; //孩子2 int child3; //孩子3 }PTNode; typedef struct //樹結構 { PTNode nodes[MAX_TREE_SIZE]; //結點數組 int r, n; //r是根位置,n是結點數 }PTree;
與雙親表示法中的改進1->表示法非常相似,只是孩子表示法注重結點的孩子。
缺點:占用了大量不必要的孩子域空指針。以其為標准:需要3n個指針域,實際上有用n-1個(除了根節點,其他n-1個都向上需要一條邊),則有2n+1個無用,浪費
改進一:為每個結點添加一個結點度域,方便控制指針域的個數
缺點:維護困難,不易實現。
改進二:結合順序結構和鏈式結構
#define MAX_TREE_SIZE 100 typedef int TElemType; typedef struct CTNode //孩子結點 { TElemType child; struct CTNode* next; }*ChildPtr; typedef struct //表頭結構 { TElemType data; ChildPtr firstchild; //頭指針,指向第一個孩子結點 }CTBox; typedef struct { CTBox nodes[MAX_TREE_SIZE]; //結點數組 int r, n; //r是根位置,n是結點數 }CTree;
改進三:添加雙親域,方便查找雙親結點(雙親孩子表示法)
#define MAX_TREE_SIZE 100 typedef int TElemType; typedef struct CTNode //孩子結點 { TElemType child; struct CTNode* next; }*ChildPtr; typedef struct //表頭結構 { TElemType data; int parent; ChildPtr firstchild; //頭指針,指向第一個孩子結點 }CTBox; typedef struct { CTBox nodes[MAX_TREE_SIZE]; //結點數組 int r, n; //r是根位置,n是結點數 }CTree;
三、孩子兄弟表示法
上面從雙親,孩子角度研究樹的結構,下面我們從樹的結點的兄弟角度來研究。任意一棵樹,他的結點的第一個孩子如果存在就是唯一結點,他的右兄弟如果存在,也是唯一的,因此,我們設置兩個指針,分別指向該結點的第一個孩子和該結點的右兄弟。
n個結點,有2n個指針域,有n-1條邊,空n+1個指針域
typedef int TElemType; typedef struct CSNode { TElemType data; struct CSNode* firstchild, rightsib; }CSNode,*CSTree;
若有需要,可以再加入一個雙親域。