【筆記】樹的表示與實現
1.樹的抽象資料型
與二叉樹一樣,在樹上規定如下基本操作,可以把樹定義成抽象資料型。
- Parent(n,T):返回樹T結點n的父親。若n是根,則返回空。
- LeftMostChild(n,T):返回樹T結點的最左兒子。
- RightSibling(n,T):返回樹T結點n的右兄弟。
- Data(n,T):返回樹T結點n的DATA域的值。
- Create(
v,T1,T2,…,Tk v,T_1,T_2,…,T_k):建立DATA域為v的根結點r,此結點有k顆子樹T1,T2,…,Tk T_1,T_2,…,T_k,且T1,T2,…,Tk T_1,T_2,…,T_k是自左而右排列的。返回以r為根的樹;若k=0,則r既是根也是葉結點。- Root(T):返回樹T的根結點。
通常可以對樹定義3種遍歷順序,假設各子樹記為
(1)先根順序
- 訪問根結點。
- 先根順序遍歷
T1 T_1 - 先根順序遍歷
T2 T_2
… - 先根順序遍歷
Tk T_k - 退出
(2)中根順序
- 中根順序遍歷
T1 T_1 - 訪問根結點。
- 中根順序遍歷
T2 T_2
… - 中根順序遍歷
Tk T_k - 退出
(3)後根順序
- 後根順序遍歷
T1 T_1 - 後根順序遍歷
T2 T_2
… - 後根順序遍歷
Tk T_k - 訪問根結點。
- 退出
樹的先根遍歷演算法:
void PreOrder(node n,TREE T)
/*按先根順序列出樹T中結點n及其所有後代的資料域的值*/
{
node c;
visit(Data(n,T));
c=LeftMostChild(n,T);
while(c!=NULL)
{
PreOrder(c,T);
c=RightSibling(c,T);
}
}
如果要按先跟順序遍歷整棵樹T,則只要呼叫PreOrder(Root(T),T)即可。
2.樹的表示
樹的陣列表示
設T是一棵樹,其中的結點命名為1,2,3,…,n。根據樹中每個結點只有一個父親這個特性,可用陣列A來表示樹T。令陣列的下標對應於結點名,陣列元素A[i]定義如下:
這種表示法除根結點外,每個結點只有一個指向其父親結點的鏈域。所以有時把這種表示法稱為樹的父連結串列示法或單連結串列示法。
一般來說,Parent(i)=A[i]。陣列元素由parent和data兩個域構成。可以把上圖表示的樹的結構描述為:
struct node
{
int parent;
char data;
};
typedef node TREE[11];
TREE T;
這種只有父鏈的表示方法,對於求父結點及祖先結點都很方便。但對於給定的結點n,欲求其子結點的資訊相當困難,也不能反映各兄弟結點的順序。但是如果在給結點命名時,可按照這樣一種順序:對每個結點n,在對n進行編號之後,按自左而右遞增的順序依次對n的兒子進行編號。在此假設之下,將很容易實現RightSibling和LeftMostChild。
typedef int node;
typedef node TREE[maxnodes];
node LeftMostChild(node n,TREE T)
{
node i;
for(i=n+1;i<=maxnodes-1;i++)
if(T[i]==n)
return i;
return 0;
}
樹的鄰接表表示
樹的一種重要表示法是每個結點的所有子結點形成一個線性表,但因每個結點所具有的子結點個數不同,所以通常都採用連結式表示。
上圖中的樹為例可以表示為下圖所示。其中有一個陣列header,每一個陣列元素header[i]是一個表頭結點,指向結點i的兒子結點連結串列的首結點。對於每個結點i,由header[i]出發,可以很容易地將其全部兒子結點找到。
鄰接表的型別說明:
typedef int node;
struct celltype
{
node element;
celltype *next;
};
typedef celltype *LIST;
typedef celltype *position;
struct TREE
{
LIST header[maxnodes];
datatype data[maxnodes];
node root;
};
用樹的這種表示法實現LeftMostChild操作:
node LeftMostChild(node n ,TREE T)
{
LIST L;
L=T.header[n];
if(Empty(L))
return 0;
else
return(Retrieve(First(L),L));
}
樹的左右連結串列示法
通常,可以按照其自然形式,令每個結點除其資訊域外,設定多個鏈域,使它們指向相應的兒子結點,但這樣會使每個結點佔用的空間較多。為了節省每個結點所佔用的空間,在每個結點中只設定兩個鏈域,令左鏈指向最左兒子,右鏈指向緊挨它的右兄弟,樹的這種儲存表示方法稱為樹的左右連結串列示法,也稱為樹的左兄弟右兒子表示法或者樹的二叉連結串列表示法。這種表示法很容易實現由較小的樹來構造較大的樹。
其儲存結構為:
struct
{
datatype data;
int leftchild;
int rightsibling;
}cellspace[maxnodes];
用樹的左右連結串列示法,很容易實現除Parent操作以外的各種操作。如果希望有效地實現Parent操作,即快速地找到一個結點的父結點,則在每個結點中增加指向父結點的鏈域。
struct
{
datatype data;
int leftchild;
int rightsibling;
int parent;
}cellspace[maxnodes];
此外,還可以選擇這樣一種結構:令最右兒子的rightsibling域指向右父親,於是必須對每個結點增加一個標誌位,用於區別其rightsibling域是指向兄弟還是指向父親。在求一個結點的父親時,只需沿著rightsibling域查到虛線的指向即可。這比在每一個結點中增加parent域可節省控制元件,但花費的時間稍多一些。
- 樹不能為空的理由
如果在設計的體系中定義了森林,同夥私又允許樹為空, 那麼由於森林是由若干顆樹構成的,很可能出現一個集合(森林)包含許多空集(樹),或者一個空集(森林)包含許多個空集(樹)的情況。如果允許樹為空,則很難確定森林中的一顆空樹對應於什麼樣的二叉樹,兩顆空樹對應於什麼樣的二叉樹。
3.樹的實現
原始碼參見樹的實現
相關文章
- 【筆記】樹、森林與二叉樹的轉換與應用筆記二叉樹
- 資料結構與演算法——表示式樹類的C++實現(二叉樹)資料結構演算法C++二叉樹
- 串的堆分配表示與實現
- <Redis設計與實現>筆記【轉】Redis筆記
- Lua設計與實現--讀書筆記筆記
- 《Redis設計與實現》讀書筆記Redis筆記
- 樹結構與Java實現Java
- Redis設計與實現學習筆記(一)Redis筆記
- 線段樹筆記筆記
- 樹莓派筆記樹莓派筆記
- 《Redis設計與實現》筆記 -- 資料結構與物件Redis筆記資料結構物件
- 現代作業系統-原理與實現【讀書筆記】作業系統筆記
- 正規表示式(筆記)筆記
- Java筆記:Lambda表示式Java筆記
- Effective c++(筆記)----類與函式之實現C++筆記函式
- 【筆記】堆及其實現筆記
- 決策樹演算法的推理與實現演算法
- *衡樹 Treap(樹堆) 學習筆記筆記
- SICP:符號求導、集合表示和Huffman樹(Python實現)符號求導Python
- 機器學習筆記--決策樹機器學習筆記
- 【筆記】哈夫曼樹筆記
- 平衡樹學習筆記筆記
- C++筆記——C++基本思想與實現(一)C++筆記
- 正規表示式速查筆記筆記
- 正規表示式筆記(四)筆記
- 正規表示式筆記(三)筆記
- 正規表示式筆記(二)筆記
- 正規表示式筆記(一)筆記
- xpath路徑表示式筆記筆記
- 【筆記】oracle 陣列實現筆記Oracle陣列
- 【c#表示式樹】最完善的表示式樹Expression.Dynamic的玩法C#Express
- 二叉樹的遍歷筆記二叉樹筆記
- 演算法筆記 - 樹的直徑演算法筆記
- C#樹的實現C#
- 【練習】樹的實現
- 最容易理解的正規表示式筆記筆記
- [筆記]laravel動態匹配路由的實現筆記Laravel路由
- [筆記]laravel定時任務的實現筆記Laravel