您的位置:首页 > 其它

遍历二叉树 - 基于队列的BFS

2017-08-28 15:51 267 查看
之前学过利用递归实现BFS二叉树搜索(http://www.cnblogs.com/webor2006/p/7262773.html),这次学习利用队列(Queue)来实现,关于什么时BFS这里不多说了,先贴张图来直观回忆下:



[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;
}
};


这里先来使用一下它,看入队跟出队的结果是否如预期:



编译运行:



对于上面的写法下面用图来将其整个过程画出来:

①、


创建一个大小为3的队列,其中head、tail指向数组0的位置,其中目前是一个size=0空数组



②、


往队列中插入元素1:



③、


往队列中插入元素2:



④、


往队列中插入元素3,这里需要注意啦!!!head会循环利用,指向数组0的位置:



⑤、


根据队列先进先出的原则拿最早放入的元素也就是tail所指向的元素:



⑥、


根据队列先进先出的原则拿最早放入的元素也就是tail所指向的元素:



⑦、


根据队列先进先出的原则拿最早放入的元素也就是tail所指向的元素:

这时注意啦!!!tail跟head一样也会循环利用,又回到index=0的位置了:



⑧、


由于此时size=0了,所以会打印出"queue is empty"。

⑨、




⑩、




⑪、


由于此时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;
}


其二叉树为:



下面具体来实现level_order:



只要理解的队列的先进先出的特点上面的实现比较容易理解,不多解释,编译运行:



下面看一下它的时间复杂度:由于队列中有多少元素就会遍历多次次,所以很明显是O(n)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: