今天學了關於樹的最最最最基本的有關概念和性質,做一下簡單的記錄:
首先,樹是什麼???
其實簡單點來說,樹就相當於一個元素之間有確定關係的集合。其中每個元素都是一個結點,他們兩兩以特定的方式連線並相互關聯,而樹就是由遞迴定義與實現的。例如,下圖就是一個典型的樹:
其中,元素1~9都是結點,而1上面沒有結點與它連線,所以它就是一個特殊的結點——樹根。
除了根結點,其他的結點能分成很多個互不相交的有限集合,而其中的幾個互相連線的結點元素就可以組成一棵子樹。
每一個結點的子樹的個數,叫做這個結點的度,其實這個結點連線的有幾個子結點它的度就是幾,比如圖中的4,他就有1個子節點,所以它的度就是1,而3下面沒有跟它連線的子結點,所以它的度就是0。
特別的,如果這個結點的度是0,那這個結點就叫做葉節點,如圖中的5,6,3,8,9就都是葉結點。
上面的結點是下面結點的父節點,下面的是上面的子結點,有相同父節點的子結點互為兄弟結點。
下面來道水題練練手:
要求先輸入兩個數 n、m,表示一棵樹的結點數和邊數,再輸入m行,每行輸入一個父節點 x 和一個x的子結點 y。現在要求輸出樹根,子結點最多的結點 和 此節點的子結點。(資料範圍忽略)
思路其實就是桶的思想,先輸入每個x和y,並記錄y的父節點x。全部輸入完之後再判斷,如果沒有父節點,那這個點就是樹根。最後再用一個迴圈巢狀遍及每一個結點,記錄他子結點的個數,找出最大值就好了。
程式碼如下:
1 #include<iostream> 2 using namespace std; 3 int n,m; 4 int tree[100]={0};//記錄父節點 5 int ans,sum,maxn; 6 int main() 7 { 8 int x,y; 9 scanf("%d%d",&n,&m); 10 for(int i=1;i<=m;i++){ 11 scanf("%d%d",&x,&y); 12 tree[y]=x; 13 } 14 for(int i=1;i<=m;i++){ 15 if(tree[i]==0){ 16 printf("%d\n",i); 17 break; 18 } 19 } 20 for(int i=1;i<=n;i++){ 21 sum=0; 22 for(int j=1;j<=n;j++){ 23 if(tree[j]==i) sum++; 24 if(sum>maxn){ 25 maxn=sum; 26 ans=i; 27 } 28 } 29 } 30 printf("%d\n",ans); 31 for(int i=1;i<=n;i++){ 32 if(tree[i]==ans){ 33 printf("%d ",i); 34 } 35 } 36 return 0; 37 }
關於樹的遍歷,有好幾種:
1、先序遍歷:先訪問根結點,再從左往右的訪問每一棵子樹,與DFS相似,在上圖中遍歷順序就是125634789
2、後續遍歷:先從左到右先遍歷每一棵子樹,最後訪問根結點,在上圖中遍歷的順序就是562389741
3、層次遍歷:一層一層地遍歷,在圖上遍歷的順序就是123456789
4、葉結點遍歷:上圖按照這個思想訪問的結果為:56389;
5、中序遍歷:左兒子——父節點——右兒子(前提是必須是二叉樹)。
關於二叉樹:
二叉樹是一種特殊的樹形結構,除了葉節點,其餘的每個節點的度都小於等於2,也就是說每個父節點都最多有兩個子結點。下圖是二叉樹的五種形態:
關於二叉樹的性質:
1、在二叉樹的第i層上最多有 2 的 i-1 次方個結點
證明:二叉樹的第一層至多有2的零次方個結點,第2層至多有2的一次方個結點,因為每個節點最多有兩個兒子,所以每一層都是上一層的結點數乘2,那第 i 層自然就是2^(i-1)個結點。
2、層數(深度)為k的二叉樹最多有 2^k -1個結點
證明:由於第1層有2^0個結點,第2層有2^1個結點,那第k層至多有2^(k-1)個結點,
則總結點數就是:2^0+2^1+……+2^(k-1)=2^0+2^0+2^1+……+2^(k-1)-2^0=2^1+2^1+2^2+……+2^(k-1)-1=2^k -1
3、如果一棵二叉樹葉結點數為n0,度為2的結點數為n2,則一定滿足:n0=n2+1
證明:對於父節點一定對應兩子結點的子樹,設一共x層,那非子結點的個數就是前x-1層的個數,由性質2得出前x-1層的個數為2^(x-1)-1,由性質1得出第x層上的子結點個數為2^(x-1)個,所以兩者相差1。
4、有n個結點的完全二叉樹(最下層的葉節點左邊是滿的,右邊可以為空,如下圖)的深度為:floor(log2 n)+1
證明:設結點數是n,深度是k,由性質2得:n=2^(k-1)為指數函式,轉換成對數函式就是:log2 n=k-1。即 k=floor((log2 n)+1),由於人家不一定是滿的,要加一個下取整。
(如圖就是一棵完全二叉樹)
5、對於任意的完全二叉樹的某個標號為n的左結點,此結點的父節點的標號為n/2,兄弟結點的標號n+1,左兒子為2n,右兒子為2n+1。
一道簡單的練習題:
q:一棵完全二叉樹的結點總數是18,其葉節點數是?
a:由性質4得出此二叉樹的層數為floor(log2 18)+1=5
由性質2的前4層(由於是完全二叉樹,前4層一定是滿的)的結點數是2^4 -1=15
所以最後一層有18-15=3個結點,最後一層的三個子結點用掉了上一層的2個父結點
第四層有2^(4-1)=8個子結點,用掉倆還剩6個沒有子結點的結點,他們也是葉結點
所以一共有3+6=9個子結點
遍歷二叉樹的程式碼實現:
1.先序遍歷:先訪問根結點,再遍歷左二叉樹,最後遍歷右二叉樹。
1 void preorder(tree bt) //先序遞迴演算法 2 { 3 if(bt) 4 { cout << bt->data; 5 preorder(bt->lchild); 6 preorder(bt->rchild); 7 } 8 }
2、中序遍歷:先遍歷左二叉樹,再訪問根結點,最後遍歷右二叉樹。
1 void inorder(tree bt) //中序遍歷遞迴演算法 2 { 3 if(bt) 4 { inorder(bt->lchild); 5 cout << bt->data; 6 inorder(bt->rchild); 7 } 8 }
3、後序遍歷:先遍歷左二叉樹,再遍歷右二叉樹,最後訪問根結點。
1 void postorder(tree bt) //後序遞迴演算法 2 { 3 if(bt) 4 { 5 postorder(bt->lchild); 6 postorder(bt->rchild); 7 cout << bt->data; 8 } 9 }
關於一棵表示式樹,可以分別用先序,中序,後序遍歷方法得到3鐘不同的遍歷結果:
字首表示(波蘭式):- + a * b - c d / e f
中綴表示:a + b * ( c - d ) - e / f
字尾表示(逆波蘭式):a b c d - * + e f / -
還有就是關於二叉樹的唯一性:
知道先序或後序其中的一個和中序就可以確定一棵樹,但是隻知道先序和後序就不可以·,比如:
二叉樹基操:
1、建樹
2、刪樹
3、插點
我來模擬一下,假設現在我有一個排好序了的二叉樹,排序規則是對於任意一個子樹根,他的左子樹上的結點都比子樹根小,右子樹上的結點都比它大。
那我現在就用一個遞迴,如果輸入的數比根結點小那就遞迴左子樹,如果大就遞迴右子樹,直到找到合適的位置就把他加進去
4、查詢
其實也跟插點的思想差不多,類似於二分查詢,找到就返回該點,找不到就返回NULL
來幾道題練練手吧
注意!!下面的操作均用到指標!!!
【問題描述】
由於先序、中序和後序序列中的任一個都不能唯一確定一棵二叉樹,所以對二叉樹做如下處理,將二叉樹的空結點用·補齊,稱為原二叉樹的擴充套件二叉樹,擴充套件二叉樹的先序和後序序列能唯一確定其二叉樹。
現給出擴充套件二叉樹的先序序列,要求輸出其中序和後序序列。
【輸入樣例】
ABD..EF..G..C..
【輸出樣例】
DBFEGAC
DFGEBCA
題解如下:
【問題描述】
以二叉連結串列作儲存結構,建立一棵二叉樹,並輸出該二叉樹的先序、中序、後序遍歷序列、高度和結點總數。
【輸入樣例】
12##3## //#為空
【輸出樣例】
123 //先序
213 //中序
231 //後序
2 //高度
3 //結點總數
題解如下:
[題目描述]
給出一棵二叉樹的中序與後序排列。求出它的先序排列。(約定樹結點用不同的大寫字母表示,長度<=8)。
【輸入格式】
2行,均為大寫字母組成的字串,表示一棵二叉樹的中序與 後序排列。
【輸出格式】
1行,表示一棵二叉樹的先序。
【輸入樣例】
BADC
BDCA
【輸出樣例】
ABCD
題解如下:
反正我是學不懂了,就先記錄到這吧
拜拜!!!