在談二叉樹前先談下樹和圖的概念html
樹:不包含迴路的連通無向圖(樹是一種簡單的非線性結構)node
樹有着不包含迴路這個特色,因此樹就被賦予了不少特性數組
一、一棵樹中任意兩個結點有且僅有惟一的一條路徑連通spa
二、一棵樹若是有n個結點,那它必定剛好有n-1條邊指針
三、在一棵樹中加一條邊將會構成一個迴路code
四、樹中有且僅有一個沒有前驅的結點稱爲根結點htm
在對樹進行討論的時候將樹中的每一個點稱爲結點,blog
根結點:沒有父結點的結點遞歸
葉結點:沒有子結點的結點
圖片
內部結點:一個結點既不是根結點也不是葉結點
每一個結點還有深度,好比上圖左邊的樹的4號結點深度是3(深度是指從根結點到這個結點的層數,根結點爲第一層)
二叉樹是一種非線性結構,二叉樹是遞歸定義的,其結點有左右子樹之分
二叉樹一般採用鏈式存儲結構,存儲結點由數據域和指針域(指針域:左指針域和右指針域)組成,二叉樹的鏈式存儲結構也稱爲二叉鏈表,對滿二叉樹和徹底二叉樹可按層次進行順序存儲
特色:
一、每一個結點最多有兩顆子樹
二、左子樹和右子樹是有順序的,次序不能顛倒
三、即便某結點只有一個子樹,也要區分左右子樹
四、二叉樹可爲空,空的二叉樹沒有結點,非空二叉樹有且僅有一個根節點
二叉樹中有兩種特殊的二叉樹:滿二叉樹、徹底二叉樹
滿二叉樹必定是徹底二叉樹,但徹底二叉樹不必定是滿二叉樹
(滿二叉樹的嚴格的定義是:一顆深度爲h且有2h-1個結點的二叉樹)
(圖片來源:https://www.cnblogs.com/polly333/p/4740355.html)
第一種解釋:若是一顆二叉樹除最右邊位置上有一個或幾個葉結點缺乏外,其餘是豐滿的那麼這樣的二叉樹就是徹底二叉樹(這句話不太好理解),看下面第二種解釋
第二種解釋:除第h層外,其餘各層(1到h-1)的結點數都達到最大個數,第h層從右向左連續缺若干結點,則這個二叉樹就是徹底二叉樹
也就是說若是一個結點有右子結點,那麼它必定也有左子結點
第三種解釋:除最後一層外,每一層上的節點數均達到最大值,在最後一層上只缺乏右邊的若干結點
徹底二叉樹的形狀相似於下圖
爲了方便理解請看下圖(我的理解:徹底二叉樹就是從上往下填結點,從左往右填,填滿了一層再填下一層)
(圖片來源:http://www.javashuo.com/article/p-cborfyti-dq.html)
結點的度:結點擁有的子樹的數目
葉子結點:度爲0的結點(tips:在任意一個二叉樹中,度爲0的葉子結點老是比度爲2的結點多一個)
分支結點:度不爲0的結點
樹的度:樹中結點的最大的度
層次:根結點的層次爲1,其他結點的層次等於該結點的雙親結點的層次加1
樹的高度:樹中結點的最大層次
性質1:在二叉樹的第k層上至多有2k-1個結點(k>=1)
性質2:在深度爲m的二叉樹至多有2m-1個結點
性質3:對任意一顆二叉樹,度爲0的結點(即葉子結點)老是比度爲2的結點多一個
性質4:具備n個結點的徹底二叉樹的深度至少爲[log2n]+1,其中[log2n]表示log2n的整數部分
存儲方式
存儲的方式和圖同樣,有鏈表和數組兩種,用數組存訪問速度快,但插入、刪除節點操做就比較費時了。實際中更多的是用鏈來表示二叉樹(下面的實現代碼使用的是鏈表)
實現代碼:
1 #include <stdio.h>
2 #include <stdlib.h>
3 #define N 10
4
5 typedef struct node 6 { 7 char data; 8 struct node *lchild; /* 左子樹 */
9 struct node *rchild; /* 右子樹 */
10
11 }BiTNode, *BiTree; 12
13 void CreatBiTree (BiTree *T) /* BiTree *T等價於 struct node **T */
14 { 15 char ch; 16
17 scanf("%c", &ch); 18 if (ch == '#') /* 當遇到#時,令樹的結點爲NULL,從而結束該分支的遞歸 */
19 { 20 *T = NULL; 21 } 22 else
23 { 24 *T = (BiTree)malloc(sizeof(BiTNode)); 25 if (*T == NULL) 26 { 27 printf("內存分配失敗"); 28 exit(0); 29 } 30 (*T)->data = ch; /* 生成結點 */
31 CreatBiTree(&(*T)->lchild); /* 構造左子樹 */
32 CreatBiTree(&(*T)->rchild); /* 構造右子樹 */
33 /* 這裏須要注意的是->的優先級比&高,因此&(*T)->lchild獲得的是lchild的地址 */
34 } 35
36 } 37 int main() 38 { 39 int level = 1; 40
41 BiTree t = NULL; 42 printf("之前序遍歷方式輸入二叉樹\n"); 43 CreatBiTree(&t); /* 傳入指針的地址 */
44 }
上面的代碼採用的是之前序遍歷方式輸入二叉樹,當輸入「#」時,指針指向NULL,說明是改結點是葉結點
二叉樹的遍歷是指不重複地訪問二叉樹中全部結點,主要指非空二叉樹,對於空二叉樹則結束返回,二叉樹的遍歷主要包括前序遍歷、中序遍歷、後序遍歷
前序遍歷:首先訪問根結點,而後遍歷左子樹,最後遍歷右子樹(根->左->右)
順序:訪問根節點->前序遍歷左子樹->前序遍歷右子樹
1 /* 以遞歸方式 前序遍歷二叉樹 */
2 void PreOrderTraverse(BiTree t, int level) 3 { 4 if (t == NULL) 5 { 6 return ; 7 } 8 printf("data = %c level = %d\n ", t->data, level); 9 PreOrderTraverse(t->lchild, level + 1); 10 PreOrderTraverse(t->rchild, level + 1); 11 }
中序遍歷:首先遍歷左子樹,而後訪問根節點,最後遍歷右子樹(左->根->右)
順序:中序遍歷左子樹->訪問根節點->中序遍歷右子樹
1 /* 以遞歸方式 中序遍歷二叉樹 */
2 void PreOrderTraverse(BiTree t, int level) 3 { 4 if (t == NULL) 5 { 6 return ; 7 } 8 PreOrderTraverse(t->lchild, level + 1); 9 printf("data = %c level = %d\n ", t->data, level); 10 PreOrderTraverse(t->rchild, level + 1); 11 }
後序遍歷:首先遍歷左子樹,而後遍歷右子樹,最後訪問根節點(左->右->根)
順序:後序遍歷左子樹->後序遍歷右子樹->訪問根節點
1 /* 以遞歸方式 後序遍歷二叉樹 */
2 void PreOrderTraverse(BiTree t, int level) 3 { 4 if (t == NULL) 5 { 6 return ; 7 } 8 PreOrderTraverse(t->lchild, level + 1); 9 PreOrderTraverse(t->rchild, level + 1); 10 printf("data = %c level = %d\n ", t->data, level); 11 }
從上面能夠看出,三種遍歷方式極其類似,只是語句 printf("data = %c level = %d\n ", t->data, level);的位置發生了變化