《编程之美:分层遍历二叉树》的另外两个实现
之前重温本书写书评时,也尝试找寻更好的编程解法。今天把另一个问题的实现和大家分享。
问题定义
给定一棵二叉树,要求按分层遍历该二叉树,即从上到下按层次访问该二叉树(每一层将单独输出一行),每一层要求访问的顺序为从左到右,并将节点依次编号。下面是一个例子:
输出:
1 2 3 4 5 6 7 8
节点的定义:
struct Node {Node *pLeft;Node *pRight;int data;
};
书上的解法
书上举出两个解法。第一个解法是用递归方式,搜寻并打印某一层的节点,再打印下一层的节点。这方法简单但时间效率不高(但不需要额外空间),因此书中亦提供了第二个解法。
书中第二个解法,使用vector容器来储存n个节点信息,并用一个游标变量last记录前一层的访问结束条件,实现如下:
void PrintNodeByLevel(Node* root) {vector<Node*> vec; // 这里我们使用STL 中的vector来代替数组,可利用到其动态扩展的属性vec.push_back(root);int cur = 0;int last = 1;while(cur < vec.size()) {Last = vec.size(); // 新的一行访问开始,重新定位last于当前行最后一个节点的下一个位置while(cur < last) {cout << vec[cur] -> data << " "; // 访问节点if(vec[cur] -> lChild) // 当前访问节点的左节点不为空则压入vec.push_back(vec[cur] -> lChild);if(vec[cur] -> rChild) // 当前访问节点的右节点不为空则压入,注意左右节点的访问顺序不能颠倒vec.push_back(vec[cur] -> rChild);cur++;}cout << endl; // 当cur == last时,说明该层访问结束,输出换行符}
}
广度优先搜索
书中没有提及,本问题其实是以广度优先搜索(breath-first search, BFS)去遍历一个树结构。广度优先搜索的典型实现是使用队列(queue)。其伪代码如下:
enqueue(Q, root)
donode = dequeue(Q)process(node) //如把内容列印for each child of nodeenqueue(Q, child)
while Q is not empty
书上的解法,事实上也使用了一个队列。但本人认为,使用vector容器,较不直觉,而且其空间复杂度是O(n)。
如果用队列去实现BFS,不处理换行,能简单翻译伪代码为C++代码:
void PrintBFS(Node* root) {queue<Node*> Q;Q.push(root);do {Node *node = Q.front();Q.pop();cout << node->data << " ";if (node->pLeft)Q.push(node->pLeft);if (node->pRight)Q.push(node->pRight);}while (!Q.empty());
}
本人觉得这样的算法实现可能比较清楚,而且空间复杂度只需O(m),m为树中最多节点的层的节点数量。最坏的情况是当二叉树为完整,m = n/2。
之后的难点在于如何换行。
本人的尝试之一
第一个尝试,利用了两个队列,一个储存本层的节点,另一个储存下层的节点。遍历本层的节点,把其子代节点排入下层队列。本层遍历完毕后,就可换行,并交换两个队列。
void PrintNodeByLevel(Node* root) {deque<Node*> Q1, Q2;Q1.push_back(root);do {do {Node* node = Q1.front();Q1.pop_front();cout << node->data << " ";if (node->pLeft)Q2.push_back(node->pLeft);if (node->pRight)Q2.push_back(node->pRight);} while (!Q1.empty());cout << endl;Q1.swap(Q2); } while(!Q1.empty());
}
本实现使用deque而不是queue,因为deque才支持swap()操作。注意,swap()是O(1)的操作,实际上只是交换指针。
这实现要用两个循环(书上的实现也是),并且用了两个队列。能够只用一个循环、一个队列么?
本人的尝试之二
换行问题其实在于如何表达一层的结束。书上采用了游标,而第一个尝试则用了两个队列。本人想到第三个可行方案,是把一个结束信号放进队列里。由于使用queue<Node*>,可以插入一个空指针去表示一层的遍历结束。
void PrintNodeByLevel(Node* root) {queue<Node*> Q;Q.push(root);Q.push(0);do {Node* node = Q.front();Q.pop();if (node) {cout << node->data << " ";if (node->pLeft)Q.push(node->pLeft);if (node->pRight)Q.push(node->pRight);}else if (!Q.empty()) {Q.push(0);cout << endl;}} while (!Q.empty());
}
这个实现的代码很贴近之前的PrintBFS(),也只有一个循环。注意一点,当发现空指针(结束信号)时,要检查队列内是否还有节点,如果没有的话还插入新的结束信号,则会做成死循环。
测试代码
void Link(Node* nodes, int parent, int left, int right) {if (left != -1)nodes[parent].pLeft = &nodes[left]; if (right != -1)nodes[parent].pRight = &nodes[right];
}void main()
{Node test1[9] = { 0 };for (int i = 1; i < 9; i++)test1[i].data = i;Link(test1, 1, 2, 3);Link(test1, 2, 4, 5);Link(test1, 3, 6, -1);Link(test1, 5, 7, 8);PrintBFS(&test1[1]);cout << endl << endl;PrintNodeByLevel(&test1[1]);cout << endl;
}
结语
第一个尝试是几个月前做的,没想到今晚写博文又想到了第二个尝试。两个尝试难分优劣,但两种思维或许也可以解决其他问题。还有其他方法么?
《编程之美:分层遍历二叉树》的另外两个实现相关推荐
- 编程之美-分层遍历二叉树方法整理
[试题描述] 递归程序: 上述程序的缺点是递归函数的调用效率较低,我们知道二叉树的深度n,则只需要调用n次PrintNodeAtLevel(): 更好的算法:
- 编程之美2.10:寻找数组中的最大值和最小值
编程之美2.10: 对于一个有N个整数组成的数组,需要比较多少次才能把最大值和最小值找出来呢? 算法的思想是: 分而治之 测试数据:---------------------------------- ...
- 编程之美2.1 求二进制中1的个数
最近一段的时间,一直在看编程之美之类的算法书籍,刚开始看编程之美,感觉到难度太大,有时候也不愿意去翻动这本书,不过,经过一段时间的修炼,我也彻底的喜欢上这本书了, 书中的算法涉及到很多方面,树,链表, ...
- C#获取二叉树深度及分层遍历二叉树
尝试了一下用C#写了一下二叉树的相关算法: 代码 #region 获取二叉树深度 static int z, d = 0; //z用于记录遍历到某节点时的深度,d ...
- 2017“编程之美”终章:AI之战勇者为王
编者按:8月15日,第六届微软"编程之美"挑战赛在选手的火热比拼中圆满落下帷幕."编程之美"挑战赛是由微软主办,面向高校学生开展的大型编程比赛.自2012年起, ...
- Java 并发编程之美:并发编程高级篇之一-chat
借用 Java 并发编程实践中的话:编写正确的程序并不容易,而编写正常的并发程序就更难了.相比于顺序执行的情况,多线程的线程安全问题是微妙而且出乎意料的,因为在没有进行适当同步的情况下多线程中各个操作 ...
- Java 并发编程之美:并发编程高级篇之一
借用 Java 并发编程实践中的话:编写正确的程序并不容易,而编写正常的并发程序就更难了.相比于顺序执行的情况,多线程的线程安全问题是微妙而且出乎意料的,因为在没有进行适当同步的情况下多线程中各个操作 ...
- 层次遍历二叉树(编程之美3.10)
问题(假定根节点位于第0层) 1. 层次遍历二叉树(每层换行分开) 2. 层次遍历二叉树指定的某层 例如 上图中 1. 1 2 3 4 5 6 7 8 2. 第三层 7 8 可以看出得出第二问的解,第 ...
- 编程之美 3.10 分层遍历二叉树
解题思路: 一开始想到递归,发现用递归很难保证节点的访问顺序.(递归对于先,中,后序遍历都有效,但是对于层次遍历似乎没有找到可行的方法) 按照层次遍历二叉树的做法,通常用一个队列来保证节点按照先入先出 ...
最新文章
- python的sklearn机器学习SVM中的NuSVC运行报错:ValueError: b'specified nu is infeasible'
- MySQL(一)MySQL基础介绍
- 关于range方法,如果你觉得python很简单就错了
- gamaredon_Gamaredon组织某样本分析
- 笨方法学python3怎么样_有个很笨的女朋友,是怎么样的体验?
- android 图标错误的是什么,如何修复:android.app.RemoteServiceException:从包中发布的错误通知*:无法创建图标:StatusBarIcon...
- 1.Windows下 PHP 开源框架 laravel 的搭建
- php点击表格单元格链接,详解PhpSpreadsheet单元格设置样式、图片、超链接等
- 移动端设计尺寸基础知识
- 发出警报声的c语言程序,1、编写一个函数能够发出警报声并打印HelloWorld!;
- Excel表格-数据统计
- 微信公众平台开发1-OAuth2.0网页授权(含源码)
- 交互设计好书推荐:【A029】[图灵交互设计丛书].简约至上:交互式设计四策略.第2版
- APICloud前端框架
- android qq右上加号,Android 模拟QQ空间小加号+用popupWindow制作spinner
- rdo远程桌面管理快捷键在哪里?
- 卷积层与BN层的融合方式
- .启动ARCGIS提示“Automation错误”
- 美国网站空间如何选择
- NFT背后的区块链之争:公链还是联盟链?