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的结点一一对应。

可以得到一般结论:满二叉树和完全二叉树是两种特殊形态的二叉树,满二叉树肯定是完全二叉树,但完全二叉树不不一定是满二叉树。

举例如下图是所示:


算法导论------------基本数据结构之二叉树_子树_02

(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


举例说明二叉链表存储过程,如下图所示:


算法导论------------基本数据结构之二叉树_算法导论_03

从图中可以看出:在还有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



具体过程如下图所示:


算法导论------------基本数据结构之二叉树_算法导论_04

(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



具体过程如下图所示:


算法导论------------基本数据结构之二叉树_二叉搜索树_05

(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



具体过程如下图所示:


算法导论------------基本数据结构之二叉树_算法导论_06

  写一个完整的程序练习二叉树的三种遍历,采用递归形式创建二叉树,然后以递归的形式遍历二叉树,后面会接着讨论如何使用非递归形式实现这三种遍历,程序采用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


程序测试结果如下:


算法导论------------基本数据结构之二叉树_二叉树_07

  现在来讨论一下如何采用非递归实现这以上三种遍历。将递归形式转换为非递归形式,引入了额外的辅助结构栈。另外在讨论一次二叉树的层次遍历,可以借助队列进行实现。具体讨论如下:

(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来判断是否访问元素。

采用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



程序测试结果如下:


算法导论------------基本数据结构之二叉树_算法导论_08