二叉樹、前序遍歷、中序遍歷、後序遍歷

1、樹

在談二叉樹前先談下樹和圖的概念html

樹:不包含迴路的連通無向圖(樹是一種簡單的非線性結構)node

樹有着不包含迴路這個特色,因此樹就被賦予了不少特性數組

一、一棵樹中任意兩個結點有且僅有惟一的一條路徑連通spa

二、一棵樹若是有n個結點,那它必定剛好有n-1條邊指針

三、在一棵樹中加一條邊將會構成一個迴路code

四、樹中有且僅有一個沒有前驅的結點稱爲根結點htm

 

在對樹進行討論的時候將樹中的每一個點稱爲結點,blog

根結點:沒有父結點的結點遞歸

葉結點:沒有子結點的結點
圖片

內部結點:一個結點既不是根結點也不是葉結點

每一個結點還有深度,好比上圖左邊的樹的4號結點深度是3(深度是指從根結點到這個結點的層數,根結點爲第一層)

 

2、二叉樹

 

基本概念:

二叉樹是一種非線性結構,二叉樹是遞歸定義的,其結點有左右子樹之分

二叉樹的存儲結構:

二叉樹一般採用鏈式存儲結構,存儲結點由數據域和指針域(指針域:左指針域和右指針域)組成,二叉樹的鏈式存儲結構也稱爲二叉鏈表,對滿二叉樹和徹底二叉樹可按層次進行順序存儲

 

特色:

一、每一個結點最多有兩顆子樹

二、左子樹和右子樹是有順序的,次序不能顛倒

三、即便某結點只有一個子樹,也要區分左右子樹

四、二叉樹可爲空,空的二叉樹沒有結點,非空二叉樹有且僅有一個根節點

二叉樹中有兩種特殊的二叉樹:滿二叉樹、徹底二叉樹

 

滿二叉樹:二叉樹中每一個內部結點都有存在左子樹和右子樹(或者說滿二叉樹全部的葉結點都有一樣的深度)

滿二叉樹必定是徹底二叉樹,但徹底二叉樹不必定是滿二叉樹

(滿二叉樹的嚴格的定義是:一顆深度爲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,說明是改結點是葉結點

 

3、二叉樹的遍歷(前序\中序\後序遍歷)

 二叉樹的遍歷是指不重複地訪問二叉樹中全部結點,主要指非空二叉樹,對於空二叉樹則結束返回,二叉樹的遍歷主要包括前序遍歷、中序遍歷、後序遍歷

前序遍歷:首先訪問根結點,而後遍歷左子樹,最後遍歷右子樹(根->左->右)

順序:訪問根節點->前序遍歷左子樹->前序遍歷右子樹

 

 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);的位置發生了變化

相關文章
相關標籤/搜索