之前学过利用递归实现BFS二叉树搜索(http://www.cnblogs.com/webor2006/p/7262773.html),这次学习利用队列(Queue)来实现,关于什么时BFS这里不多说了,先贴张图来直观回忆下:
实现一个队列【采用数组实现】:
这时同样采用模版技术可以往队列中去放任何类型的数据,而这个队列同样是声明在头文件中,以便在需要用到队列的地方去随时包含它,下面先来定个初步框架:
/** * 利用数组来实现队列 */ 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以便拿下一个元素,因为是先进先出原则。
基于上节的实现来利用队列实现二叉树的BFS遍历:
先贴一下上节的代码,基于这个二叉树利用队列去实现:
#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)。