關於樹(圖論初步)

你的小垃圾發表於2022-03-24

今天學了關於樹的最最最最基本的有關概念和性質,做一下簡單的記錄:

首先,樹是什麼???

其實簡單點來說,樹就相當於一個元素之間有確定關係的集合。其中每個元素都是一個結點,他們兩兩以特定的方式連線並相互關聯,而樹就是由遞迴定義與實現的。例如,下圖就是一個典型的樹:

 

其中,元素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

題解如下:

 

 

反正我是學不懂了,就先記錄到這吧

拜拜!!!

 

 

            

 

相關文章