摘要

  书中第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语言实现,完整程序如下:

View Code

 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实现,完整程序如下所示:

View Code

  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 }

程序测试结果如下:

转载于:https://www.cnblogs.com/Anker/archive/2013/01/27/2878594.html

《算法导论》读书笔记之第10章 基本数据结构之二叉树相关推荐

  1. 算法导论读书笔记-第十九章-斐波那契堆

    算法导论第19章--斐波那契堆 可合并(最小)堆(mergeable min-heap) : 支持以下5种操作的一种数据结构, 其中每一个元素都有一个关键字: MAKE-HEAP(): 创建和返回一个 ...

  2. 算法导论读书笔记(8)

    算法导论读书笔记(8) 目录 计数排序 计数排序的简单Java实现 基数排序 基数排序的简单Java实现 桶排序 计数排序 计数排序 假设 n 个输入元素中的每一个都是介于0到 k 之间的整数,此处 ...

  3. 算法导论读书笔记(19)

    http://www.cnblogs.com/sungoshawk/p/3802553.html 算法导论读书笔记(19) 目录 最优二叉搜索树 步骤1:一棵最优二叉查找树的结构 步骤2:一个递归解 ...

  4. 算法导论读书笔记(7)

    算法导论读书笔记(7) 目录 快速排序 快速排序的简单Java实现 快速排序的性能 最坏情况划分 最佳情况划分 快速排序的随机化版本 比较排序 快速排序 快速排序是一种原地排序算法,对包含 n 个数的 ...

  5. 《ASP.NET Core 微服务实战》-- 读书笔记(第10章)

    第 10 章 应用和微服务安全 云应用意味着应用运行所在的基础设施无法掌控,因此安全不能再等到事后再考虑,也不能只是检查清单上毫无意义的复选框 由于安全与云原生应用密切相关,本章将讨论安全话题,并用示 ...

  6. 算法导论读书笔记 第4章 分治策略

    在第2章中,归并排序算法使用了分治策略.即在分治策略中,递归地求解一个问题,在每层递归中应包含三个步骤: 分解(Divide)步骤将问题画分为一些子问题,子问题的形式与原问题一样,只是规模更小. 解决 ...

  7. 【算法导论学习笔记】第3章:函数的增长

    原创博客,转载请注明: http://www.cnblogs.com/wuwenyan/p/4982713.html  当算法的输入n非常大的时候,对于算法复杂度的分析就显得尤为重要,虽然有时我们能通 ...

  8. 算法导论读书笔记(20)van Emde Boas树

    第五部分 高级数据结构 第20章 van Emde Boas树 van Emde Boas树支持优先队列操作以及一些其他操作,每个操作最坏情况运行时间为O(lglgn).而这种数据结构限制关键字必须为 ...

  9. linux pipe函数 重定向,I/O重定向和管道——《Unix/Linux编程实践教程》读书笔记(第10章)...

    1.I/O重定向的概念与原因 及 标准输入.输出的标准错误的定义 所以的Unix I/O重定向都基于标准数据流的原理.三个数据了分别如下: 1)标准输入--需要处理的数据流 2)标准输出--结果数据流 ...

最新文章

  1. c语言spi发送12位数据,【51单片机】普通I/O口模拟SPI口C语言程序
  2. 【317】python 指定浏览器打开网页 / 文件
  3. Django 用 uploadify 实现图片批量上传
  4. C++ 解析Json
  5. Xcode + cocoapods + jenkins + fastlane + git + 蒲公英自动化打包 上传
  6. 数据库内存泄漏——A SQLiteConnection object for database '/data/data/.../databases/....db' was leaked!...
  7. 如何在GitHub上发现优秀的开源项目
  8. tensorflow实战学习笔记(1)
  9. ABP中module-zero快速集成微信用户认证
  10. 图的BFS和DFS原理及实例分析(java)
  11. lvgl8.2 分析画面刷新
  12. 天涯社区脱水机(TianyaTool) - 实现天涯社区宝箱的只看楼主功能
  13. 直观认识Windows
  14. drhf赫夫生医的产品质量怎么样?
  15. javascript 取整,取余数
  16. Vue Element正则验证(所有)
  17. org.quartz.JobPersistenceException: Couldn‘t store job:
  18. 使用 PLINK 把 vcf 的0/0,0/1,1/1转为字母格式的基因型(比如AA,AG,GG)
  19. 浅谈“跨站跟踪攻击(即CST/XST攻击)”
  20. 三维家可以导入别人的方案吗_三维家辅助工具如何使用?三维家辅助工具安装使用教程...

热门文章

  1. 聊聊 API Gateway 和 Netflix Zuul
  2. 你真的懂病毒式营销吗
  3. Nagios学习实践系列——配置研究[监控当前服务器]
  4. 几行小代码,将Testlink的xml用例导入至excel
  5. SQL Server调优系列基础篇(常用运算符总结)
  6. javascript基础知识总结
  7. 如何给一个文件重命名?
  8. redhat as4 上安装 MySQL5
  9. PTA:7-1 还原二叉树 (25 分)
  10. 大数据可视化需注意什么问题