遍历二叉树 - 基于队列的BFS
2017-08-28 15:51
267 查看
之前学过利用递归实现BFS二叉树搜索(http://www.cnblogs.com/webor2006/p/7262773.html),这次学习利用队列(Queue)来实现,关于什么时BFS这里不多说了,先贴张图来直观回忆下:
![](https://images2017.cnblogs.com/blog/324374/201708/324374-20170806161414319-817900806.gif)
[b]实现一个队列【采用数组实现】:[/b]
这时同样采用模版技术可以往队列中去放任何类型的数据,而这个队列同样是声明在头文件中,以便在需要用到队列的地方去随时包含它,下面先来定个初步框架:
具体实现如下:
这里先来使用一下它,看入队跟出队的结果是否如预期:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909141342944-1572815876.png)
编译运行:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909141410007-1719818644.png)
对于上面的写法下面用图来将其整个过程画出来:
①、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909141931804-1675484944.png)
创建一个大小为3的队列,其中head、tail指向数组0的位置,其中目前是一个size=0空数组
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909143952241-2124004019.png)
②、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909141954819-1407769768.png)
往队列中插入元素1:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909145740726-651688775.png)
③、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909142047679-1171772953.png)
往队列中插入元素2:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909145639804-491496365.png)
④、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909142157272-86712210.png)
往队列中插入元素3,这里需要注意啦!!!head会循环利用,指向数组0的位置:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909150437835-1404462511.png)
⑤、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909142208929-1398496745.png)
根据队列先进先出的原则拿最早放入的元素也就是tail所指向的元素:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909152621007-974674630.png)
⑥、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909142219319-488082086.png)
根据队列先进先出的原则拿最早放入的元素也就是tail所指向的元素:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909152813241-727720918.png)
⑦、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909142219319-488082086.png)
根据队列先进先出的原则拿最早放入的元素也就是tail所指向的元素:
这时注意啦!!!tail跟head一样也会循环利用,又回到index=0的位置了:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909153226569-2145396321.png)
⑧、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909142310507-264280224.png)
由于此时size=0了,所以会打印出"queue is empty"。
⑨、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909142325757-906818793.png)
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909153551319-2074965882.png)
⑩、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909142346632-1644483874.png)
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909153913663-1767005728.png)
⑪、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909142310507-264280224.png)
由于此时size=0了,所以会打印出"queue is empty"。
总结:
1、添加元素放到head处,添加完成之后将head+1以便下次进行存放。
2、拿元素时是从tail处拿,拿完之后也是将tail+1以便拿下一个元素,因为是先进先出原则。
[b]基于上节的实现来利用队列实现二叉树的BFS遍历:[/b]
先贴一下上节的代码,基于这个二叉树利用队列去实现:
其二叉树为:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909155302679-1503187255.png)
下面具体来实现level_order:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909155440632-1531393265.png)
只要理解的队列的先进先出的特点上面的实现比较容易理解,不多解释,编译运行:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909155503226-400970151.png)
下面看一下它的时间复杂度:由于队列中有多少元素就会遍历多次次,所以很明显是O(n)。
![](https://images2017.cnblogs.com/blog/324374/201708/324374-20170806161414319-817900806.gif)
[b]实现一个队列【采用数组实现】:[/b]
这时同样采用模版技术可以往队列中去放任何类型的数据,而这个队列同样是声明在头文件中,以便在需要用到队列的地方去随时包含它,下面先来定个初步框架:
/** * 利用数组来实现队列 */ template <typename T> class queue { T* data;//用数组来实现队列 int head/* 头指向的位置 */, tail/* 尾指向的位置 */, size/* 当前队列存放的元素个数 */, data_length/* 总队列的大小 */; public: queue(int length):head(0), tail(0), size(0), data_length(length) { data = new T[length]; } //入队列 void push(T value) { } //出队列 void pop() { } //取出队列最早的元素 T top() { } //判断队列是否为空 bool isEmpty() { } };
具体实现如下:
/** * 利用数组来实现队列 */ template <typename T> class queue { T* data;//用数组来实现队列 int head/* 头指向的位置 */, tail/* 尾指向的位置 */, size/* 当前队列存放的元素个数 */, data_length/* 总队列的大小 */; public: queue(int length):head(0), tail(0), size(0), data_length(length) { data = new T[length]; } //入队列 void push(T value) { if(size == data_length) { throw "queue is full";//如果队列已经满了则直接抛异常,实际可以去将数组扩容,这里简单处理,重在学习数据结构 } data[head] = value; head = (head + 1) % data_length;//这是为了循环利用,如果队列还有空间的话 size++; } //出队列 void pop() { if(isEmpty()) { throw "queue is empty"; } tail = (tail + 1) % data_length;//这是为了循环利用 size--; } //取出队列最早的元素 T top() { if(isEmpty()) throw "You cannot get the top element from an empty queue"; return data[tail]; } //判断队列是否为空 bool isEmpty() { return size == 0; } };
这里先来使用一下它,看入队跟出队的结果是否如预期:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909141342944-1572815876.png)
编译运行:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909141410007-1719818644.png)
对于上面的写法下面用图来将其整个过程画出来:
①、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909141931804-1675484944.png)
创建一个大小为3的队列,其中head、tail指向数组0的位置,其中目前是一个size=0空数组
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909143952241-2124004019.png)
②、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909141954819-1407769768.png)
往队列中插入元素1:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909145740726-651688775.png)
③、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909142047679-1171772953.png)
往队列中插入元素2:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909145639804-491496365.png)
④、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909142157272-86712210.png)
往队列中插入元素3,这里需要注意啦!!!head会循环利用,指向数组0的位置:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909150437835-1404462511.png)
⑤、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909142208929-1398496745.png)
根据队列先进先出的原则拿最早放入的元素也就是tail所指向的元素:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909152621007-974674630.png)
⑥、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909142219319-488082086.png)
根据队列先进先出的原则拿最早放入的元素也就是tail所指向的元素:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909152813241-727720918.png)
⑦、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909142219319-488082086.png)
根据队列先进先出的原则拿最早放入的元素也就是tail所指向的元素:
这时注意啦!!!tail跟head一样也会循环利用,又回到index=0的位置了:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909153226569-2145396321.png)
⑧、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909142310507-264280224.png)
由于此时size=0了,所以会打印出"queue is empty"。
⑨、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909142325757-906818793.png)
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909153551319-2074965882.png)
⑩、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909142346632-1644483874.png)
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909153913663-1767005728.png)
⑪、
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909142310507-264280224.png)
由于此时size=0了,所以会打印出"queue is empty"。
总结:
1、添加元素放到head处,添加完成之后将head+1以便下次进行存放。
2、拿元素时是从tail处拿,拿完之后也是将tail+1以便拿下一个元素,因为是先进先出原则。
[b]基于上节的实现来利用队列实现二叉树的BFS遍历:[/b]
先贴一下上节的代码,基于这个二叉树利用队列去实现:
#include <iostream> #include "stack.h" #include "queue.h" //用来表示二叉树 struct treenode{ int data; treenode* left;//左结点 treenode* right;//右结点 treenode(int value):data(value), left(nullptr), right(nullptr){} }; //前序遍历 void pre_order(treenode* root){ Stack<treenode*> stack;//声明一个栈 treenode* current_node = root; while(current_node) { //1、首先打印当前结点,因为是前序遍历 std::cout << current_node->data << std::endl; //2、如果存在右结点则将其入栈暂存,待左结点输出完之后再去处理这些右结点 if(current_node->right) stack.push(current_node->right); //3、不断去处理左结点,直到左结点处理完了,则从栈中拿右点进行处理 if(current_node->left)//如果有左结点,则将它做为当前处理的结点不断输出 current_node = current_node->left; else { //这时左结点已经处理完了 if(stack.isEmpty())//如果缓存栈已经为空了则说明整个二叉树的遍历结束了 current_node = nullptr; else { //则取出栈顶的右结点进行处理,由于是后进先出,所以拿出来的永远是最新插入的右结点 current_node = stack.top(); stack.pop();//将其元素从栈顶弹出 } } } } //利用队列实现BFS void level_order(treenode* root){ //TODO } int main(void) { //构建二叉树: //1、第一层根结点 treenode* root = new treenode(5); //2、第二层结点 root->left = new treenode(3); root->right = new treenode(8); //3、第三层结点 root->left->left = new treenode(1); root->left->right = new treenode(4); root->right->left = new treenode(7); //4、第四层结点 root->right->left->left = new treenode(6); pre_order(root); std::cout << "##################" << std::endl; level_order(root); return 0; }
其二叉树为:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909155302679-1503187255.png)
下面具体来实现level_order:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909155440632-1531393265.png)
只要理解的队列的先进先出的特点上面的实现比较容易理解,不多解释,编译运行:
![](https://images2017.cnblogs.com/blog/324374/201709/324374-20170909155503226-400970151.png)
下面看一下它的时间复杂度:由于队列中有多少元素就会遍历多次次,所以很明显是O(n)。
相关文章推荐
- 基于三叉链表存储结构的二叉树的不用栈的非递归遍历
- 队列实现二叉树的层序遍历
- 基于jdk的list 和 队列 linkedList 和父亲长子兄弟链表模型 实现的 树 的前序遍历,后序遍历和层次遍历
- 102.LeetCode Binary Tree Level Order Traversal(easy)[二叉树层次遍历 广度搜索 队列]
- 二叉树前中后序遍历的递归版本和非递归版本、队列实现的层次遍历
- 数据结构实验之二叉树五:层序遍历(二叉树+队列)
- 利用队列实现二叉树的层次遍历
- 二叉树层序遍历,不用定义队列
- 队列和堆栈实现二叉树的遍历
- 遍历二叉树 - 基于递归的DFS(前序,中序,后序)
- 二叉树按层次遍历--队列实现
- 算法学习笔记(六) 二叉树和图遍历—深搜 DFS 与广搜 BFS
- C++非递归队列实现二叉树的广度优先遍历
- 6-3-2 二叉树层次遍历(BFS)
- 遍历二叉树 - 基于栈的DFS
- 基于邻接表存储的图的DFS与BFS遍历
- 二叉树按层次遍历--队列实现
- LeetCode Binary Tree Right Side View : 思想上的基于队列的广度优先遍历,形式上的一个简单变种
- 模板二叉树的创建与深度遍历(栈、递归)和广度优先遍历(队列)
- 二叉树的层次遍历(BFS),二叉树的所有路径,二叉树的最大路径和(分治)