摘要
書中第10章10.4小節介紹了有根樹,簡單介紹了二叉樹和分支數目無限制的有根樹的存儲結構,而沒有關於二叉樹的遍歷過程。為此對二叉樹做個簡單的總結,介紹一下二叉樹基本概念、性質、二叉樹的存儲結構和遍歷過程,主要包括先根遍歷、中根遍歷、后根遍歷和層次遍歷。
1、二叉樹的定義
二叉樹(Binary Tree)是一種特殊的樹型結構,每個節點至多有兩棵子樹,且二叉樹的子樹有左右之分,次序不能顛倒。
由定義可知,二叉樹中不存在度(結點擁有的子樹數目)大於2的節點。二叉樹形狀如下下圖所示:
2、二叉樹的性質
(1)在二叉樹中的第i層上至多有2^(i-1)個結點(i>=1)。備注:^表示此方
(2)深度為k的二叉樹至多有2^k-1個節點(k>=1)。
(3)對任何一棵二叉樹T,如果其終端結點數目為n0,度為2的節點數目為n2,則n0=n2+1。
滿二叉樹:深度為k且具有2^k-1個結點的二叉樹。即滿二叉樹中的每一層上的結點數都是最大的結點數。
完全二叉樹:深度為k具有n個結點的二叉樹,當且僅當每一個結點與深度為k的滿二叉樹中的編號從1至n的結點一一對應。
可以得到一般結論:滿二叉樹和完全二叉樹是兩種特殊形態的二叉樹,滿二叉樹肯定是完全二叉樹,但完全二叉樹不不一定是滿二叉樹。
舉例如下圖是所示:
(4)具有n個節點的完全二叉樹的深度為log2n + 1。
3、二叉樹的存儲結構
可以采用順序存儲數組和鏈式存儲二叉鏈表兩種方法來存儲二叉樹。經常使用的二叉鏈表方法,因為其非常靈活,方便二叉樹的操作。二叉樹的二叉鏈表存儲結構如下所示:
1 typedef struct binary_tree_node 2 { 3 int elem; 4 struct binary_tree_node *left; 5 struct binary_tree_node *right; 6 }binary_tree_node,*binary_tree;
舉例說明二叉鏈表存儲過程,如下圖所示:
從圖中可以看出:在還有n個結點的二叉鏈表中有n+1個空鏈域。
4、遍歷二叉樹
遍歷二叉樹是按照指定的路徑方式訪問書中每個結點一次,且僅訪問一次。由二叉樹的定義,我們知道二叉數是由根結點、左子樹和右子樹三部分構成的。通常遍歷二叉樹是從左向右進行,因此可以得到如下最基本的三種遍歷方法:
(1)先根遍歷(先序遍歷):如果二叉樹為空,進行空操作;否則,先訪問根節點,然后先根遍歷左子樹,最后先根遍歷右子樹。采用遞歸形式實現代碼如下:
1 void preorder_traverse_recursive(binary_tree root) 2 { 3 if(NULL != root) 4 { 5 printf("%d\t",root->elem); 6 preorder_traverse_recursive(root->left); 7 preorder_traverse_recursive(root->right); 8 } 9 }
具體過程如下圖所示:
(2)中根遍歷(中序遍歷):如果二叉樹為空,進行空操作;否則,先中根遍歷左子樹,然后訪問根結點,最后中根遍歷右子樹。遞歸過程實現代碼如下:
1 void inorder_traverse_recursive(binary_tree root) 2 { 3 if(NULL != root) 4 { 5 inorder_traverse_recursive(root->left); 6 printf("%d\t",root->elem); 7 inorder_traverse_recursive(root->right); 8 } 9 }
具體過程如下圖所示:
(3)后根遍歷(后序遍歷):如果二叉樹為空,進行空操作;否則,先后根遍歷左子樹,然后后根遍歷右子樹,最后訪問根結點。遞歸實現代碼如下:
1 void postorder_traverse_recursive(binary_tree root) 2 { 3 if(NULL != root) 4 { 5 postorder_traverse_recursive(root->left); 6 postorder_traverse_recursive(root->right); 7 printf("%d\t",root->elem); 8 } 9 }
具體過程如下圖所示:
寫一個完整的程序練習二叉樹的三種遍歷,采用遞歸形式創建二叉樹,然后以遞歸的形式遍歷二叉樹,后面會接着討論如何使用非遞歸形式實現這三種遍歷,程序采用C語言實現,完整程序如下:

1 #include <stdio.h> 2 #include <stdlib.h> 3 4 //the structure of binary tree 5 typedef struct binary_tree_node 6 { 7 int elem; 8 struct binary_tree_node *left; 9 struct binary_tree_node *right; 10 }binary_tree_node,*binary_tree; 11 12 void init_binary_tree(binary_tree *root); 13 void create_binary_tree(binary_tree *root); 14 15 //previous root 16 void preorder_traverse_recursive(binary_tree root); 17 //inorder root 18 void inorder_traverse_recursive(binary_tree root); 19 //post order root 20 void postorder_traverse_recursive(binary_tree root); 21 22 int main() 23 { 24 binary_tree root; 25 init_binary_tree(&root); 26 create_binary_tree(&root); 27 preorder_traverse_recursive(root); 28 inorder_traverse_recursive(root); 29 postorder_traverse_recursive(root); 30 exit(0); 31 } 32 33 void init_binary_tree(binary_tree *root) 34 { 35 *root = NULL; 36 } 37 38 void create_binary_tree(binary_tree* root) 39 { 40 int elem; 41 printf("Enter the node value(0 is end): "); 42 scanf("%d",&elem); 43 if(elem == 0) 44 *root = NULL; 45 else 46 { 47 *root = (binary_tree)malloc(sizeof(binary_tree_node)); 48 if(NULL == root) 49 { 50 printf("malloc error.\n"); 51 exit(-1); 52 } 53 (*root)->elem = elem; 54 printf("Creating the left child node.\n"); 55 create_binary_tree(&((*root)->left)); 56 printf("Createing the right child node.\n"); 57 create_binary_tree(&((*root)->right)); 58 } 59 } 60 61 void preorder_traverse_recursive(binary_tree root) 62 { 63 if(NULL != root) 64 { 65 printf("%d\t",root->elem); 66 preorder_traverse_recursive(root->left); 67 preorder_traverse_recursive(root->right); 68 } 69 } 70 71 void inorder_traverse_recursive(binary_tree root) 72 { 73 if(NULL != root) 74 { 75 inorder_traverse_recursive(root->left); 76 printf("%d\t",root->elem); 77 inorder_traverse_recursive(root->right); 78 } 79 } 80 81 void postorder_traverse_recursive(binary_tree root) 82 { 83 if(NULL != root) 84 { 85 postorder_traverse_recursive(root->left); 86 postorder_traverse_recursive(root->right); 87 printf("%d\t",root->elem); 88 } 89 }
程序測試結果如下:
現在來討論一下如何采用非遞歸實現這以上三種遍歷。將遞歸形式轉換為非遞歸形式,引入了額外的輔助結構棧。另外在討論一次二叉樹的層次遍歷,可以借助隊列進行實現。具體討論如下:
(1)先根遍歷非遞歸實現
先根遍歷要求順序是根左右,可以借助棧s來實現。先將根root入棧,然后循環判斷s是否為空,非空則將結點出棧,記為節點p,然后依次先將結點p的右子樹結點入棧,然后將結點p的左子樹結點入棧,循環操作直到棧中所有元素都出棧為止,出棧順序即是先根遍歷的結果。采用C++中模板庫stack實現先根遍歷如下:
1 void preorder_traverse(binary_tree root) 2 { 3 if(NULL != root) 4 { 5 stack<binary_tree_node*> s; 6 binary_tree_node *ptmpnode; 7 s.push(root); 8 while(!s.empty()) 9 { 10 ptmpnode = s.top(); 11 cout<<ptmpnode->elem<<" "; 12 s.pop(); 13 if(NULL != ptmpnode->right) 14 s.push(ptmpnode->right); 15 if(NULL != ptmpnode->left) 16 s.push(ptmpnode->left); 17
18 } 19 } 20 }
(2)中根遍歷非遞歸實現
中根遍歷要求順序是左根右,借助棧s實現。先將根root入棧,接着從根root開始查找最左的子孩子結點直到為空為止,然后將空節點出棧,再將左子樹節點出棧遍歷,然后判斷該左子樹的右子樹節點入棧。循環此過程,直到棧為空為止。此時需要注意的是入棧過程中空結點也入棧了,用以判斷左孩子是否結束和左孩子是否有右孩子結點。采用C++中模板庫stack實現先根遍歷如下:
1 void inorder_traverse(binary_tree root) 2 { 3 if(NULL != root) 4 { 5 stack<binary_tree_node*> s; 6 binary_tree_node *ptmpnode; 7 s.push(root); 8 while(!s.empty()) 9 { 10 ptmpnode = s.top(); 11 while(NULL != ptmpnode) 12 { 13 s.push(ptmpnode->left); 14 ptmpnode = s.top(); 15 } 16 s.pop();//空結點出棧
17 if(!s.empty()) 18 { 19 ptmpnode = s.top(); 20 cout<<ptmpnode->elem<<" "; 21 s.pop(); 22 //右子樹結點如棧
23 s.push(ptmpnode->right); 24 } 25 } 26 } 27 }
另外一種簡潔的實現方法如下:
1 void inorder_traverse_two(binary_tree root) 2 { 3 if(NULL != root) 4 { 5 stack<binary_tree_node*> s; 6 binary_tree_node *ptmpnode; 7 ptmpnode = root; 8 while(NULL != ptmpnode || !s.empty()) 9 { 10 //將左子樹結點入棧
11 if(NULL != ptmpnode) 12 { 13 s.push(ptmpnode); 14 ptmpnode = ptmpnode->left; 15 } 16 else
17 { 18 //出棧遍歷
19 ptmpnode = s.top(); 20 s.pop(); 21 cout<<ptmpnode->elem<<" "; 22 //右子樹結點
23 ptmpnode = ptmpnode->right; 24 } 25 } 26 } 27 }
(3)后根遍歷遞歸實現
后根遍歷要求訪問順序是左右根,采用輔助棧實現時,需要一個標記,判斷結點是否訪問了,因為右子樹是通過跟結點的信息得到的。實現過程是先將根結點及其左子樹入棧,並初始標記為0,表示沒有訪問,然后通過判斷棧是否為空和標記的值是否為1來判斷是否訪問元素。
參考:http://www.cnblogs.com/hicjiajia/archive/2010/08/27/1810055.html
采用C++模板庫stack具體實現程序如下:
1 void postorder_traverse(binary_tree root) 2 { 3 if(NULL != root) 4 { 5 stack<binary_tree_node*> s; 6 binary_tree_node *ptmpnode; 7 int flags[100]; 8 ptmpnode = root; 9 while(NULL != ptmpnode || !s.empty()) 10 { 11 //將結點左子樹結點入棧 12 while(NULL != ptmpnode) 13 { 14 s.push(ptmpnode); 15 flags[s.size()] = 0; //標記未訪問 16 ptmpnode=ptmpnode->left; 17 } 18 //輸出訪問的結點 19 while(!s.empty() && flags[s.size()] == 1) 20 { 21 ptmpnode = s.top(); 22 s.pop(); 23 cout<<ptmpnode->elem<<" "; 24 } 25 //從右子樹開始遍歷 26 if(!s.empty()) 27 { 28 ptmpnode = s.top(); 29 flags[s.size()] = 1; //登記訪問了 30 ptmpnode = ptmpnode->right; 31 } 32 else 33 break; 34 } 35 } 36 }
(4)層次遍歷實現
層次遍歷要求從根向下、從左向右進行訪問,可以采用隊列實現。先將根入隊,然后隊列進程出隊操作訪問結點p,再將結點p的左子樹和右子樹結點入隊,循環執行此過程直到隊列為空。出隊順序即是層次遍歷結果。采用C++的模板庫queue實現如下:
1 void levelorder_traverse(binary_tree root) 2 { 3 if(NULL != root) 4 { 5 queue<binary_tree_node*> q; 6 binary_tree_node *ptmpnode; 7 q.push(root); 8 while(!q.empty()) 9 { 10 ptmpnode = q.front(); 11 q.pop(); 12 cout<<ptmpnode->elem<<" "; 13 if(NULL != ptmpnode->left) 14 q.push(ptmpnode->left); 15 if(NULL != ptmpnode->right) 16 q.push(ptmpnode->right); 17 } 18 } 19 }
綜合上面的分析過程寫個完整的程序測試二叉樹遍歷的非遞歸實現,采用C++語言,借助stack和queue實現,完整程序如下所示:

1 #include <iostream> 2 #include <stack> 3 #include <queue> 4 #include <cstdlib> 5 using namespace std; 6 7 typedef struct binary_tree_node 8 { 9 int elem; 10 struct binary_tree_node *left; 11 struct binary_tree_node *right; 12 }binary_tree_node,*binary_tree; 13 14 void init_binary_tree(binary_tree *root); 15 void create_binary_tree(binary_tree *root); 16 void preorder_traverse(binary_tree root); 17 void inorder_traverse(binary_tree root); 18 void inorder_traverse_two(binary_tree root); 19 void postorder_traverse(binary_tree root); 20 void levelorder_traverse(binary_tree root); 21 22 int main() 23 { 24 binary_tree root; 25 create_binary_tree(&root); 26 cout<<"preodrer traverse: "; 27 preorder_traverse(root); 28 cout<<"\ninodrer traverse: "; 29 inorder_traverse_two(root); 30 cout<<"\npostodrer traverse: "; 31 postorder_traverse(root); 32 cout<<"\nleverorder traverse: "; 33 levelorder_traverse(root); 34 exit(0); 35 } 36 37 void init_binary_tree(binary_tree *root) 38 { 39 *root = NULL; 40 } 41 42 void create_binary_tree(binary_tree* root) 43 { 44 int elem; 45 cout<<"Enter the node value(0 is end): "; 46 cin>>elem; 47 if(elem == 0) 48 *root = NULL; 49 else 50 { 51 *root = (binary_tree)malloc(sizeof(binary_tree_node)); 52 if(NULL == root) 53 { 54 cout<<"malloc error.\n"; 55 exit(-1); 56 } 57 (*root)->elem = elem; 58 cout<<"Creating the left child node.\n"; 59 create_binary_tree(&((*root)->left)); 60 cout<<"Createing the right child node.\n"; 61 create_binary_tree(&((*root)->right)); 62 } 63 } 64 65 void preorder_traverse(binary_tree root) 66 { 67 if(NULL != root) 68 { 69 stack<binary_tree_node*> s; 70 binary_tree_node *ptmpnode; 71 s.push(root); 72 while(!s.empty()) 73 { 74 ptmpnode = s.top(); 75 cout<<ptmpnode->elem<<" "; 76 s.pop(); 77 if(NULL != ptmpnode->right) 78 s.push(ptmpnode->right); 79 if(NULL != ptmpnode->left) 80 s.push(ptmpnode->left); 81 82 } 83 } 84 } 85 void inorder_traverse(binary_tree root) 86 { 87 if(NULL != root) 88 { 89 stack<binary_tree_node*> s; 90 binary_tree_node *ptmpnode; 91 s.push(root); 92 while(!s.empty()) 93 { 94 ptmpnode = s.top(); 95 while(NULL != ptmpnode) 96 { 97 s.push(ptmpnode->left); 98 ptmpnode = s.top(); 99 } 100 s.pop(); 101 if(!s.empty()) 102 { 103 ptmpnode = s.top(); 104 cout<<ptmpnode->elem<<" "; 105 s.pop(); 106 s.push(ptmpnode->right); 107 } 108 } 109 } 110 } 111 112 void inorder_traverse_two(binary_tree root) 113 { 114 if(NULL != root) 115 { 116 stack<binary_tree_node*> s; 117 binary_tree_node *ptmpnode; 118 ptmpnode = root; 119 while(NULL != ptmpnode || !s.empty()) 120 { 121 //將左子樹結點入棧 122 if(NULL != ptmpnode) 123 { 124 s.push(ptmpnode); 125 ptmpnode = ptmpnode->left; 126 } 127 else 128 { 129 //出棧遍歷 130 ptmpnode = s.top(); 131 s.pop(); 132 cout<<ptmpnode->elem<<" "; 133 //右子樹結點 134 ptmpnode = ptmpnode->right; 135 } 136 } 137 } 138 } 139 140 void postorder_traverse(binary_tree root) 141 { 142 if(NULL != root) 143 { 144 stack<binary_tree_node*> s; 145 binary_tree_node *ptmpnode; 146 int flags[100]; 147 ptmpnode = root; 148 while(NULL != ptmpnode || !s.empty()) 149 { 150 //將結點左子樹結點入棧 151 while(NULL != ptmpnode) 152 { 153 s.push(ptmpnode); 154 flags[s.size()] = 0; //標記未訪問 155 ptmpnode=ptmpnode->left; 156 } 157 //輸出訪問的結點 158 while(!s.empty() && flags[s.size()] == 1) 159 { 160 ptmpnode = s.top(); 161 s.pop(); 162 cout<<ptmpnode->elem<<" "; 163 } 164 //從右子樹開始遍歷 165 if(!s.empty()) 166 { 167 ptmpnode = s.top(); 168 flags[s.size()] = 1; //登記訪問了 169 ptmpnode = ptmpnode->right; 170 } 171 else 172 break; 173 } 174 } 175 } 176 void levelorder_traverse(binary_tree root) 177 { 178 if(NULL != root) 179 { 180 queue<binary_tree_node*> q; 181 binary_tree_node *ptmpnode; 182 q.push(root); 183 while(!q.empty()) 184 { 185 ptmpnode = q.front(); 186 q.pop(); 187 cout<<ptmpnode->elem<<" "; 188 if(NULL != ptmpnode->left) 189 q.push(ptmpnode->left); 190 if(NULL != ptmpnode->right) 191 q.push(ptmpnode->right); 192 } 193 } 194 }
程序測試結果如下: