数据结构—树和二叉树-2.二叉树的层次遍历(BFS)

来源:互联网 发布:sql replace 去除空格 编辑:程序博客网 时间:2024/05/16 18:54

上一篇文章对二叉树做了一个简单的小练习,也是对二叉树有了一个大概的了解,接下来这一篇便是二叉树中即为关键的算法——二叉树的层次遍历,即BFS
这个算法的重要性呢不再多说,主要用于求单源最短路径问题(无权值,即单权值的树)。


BFS——宽度优先遍历

顾名思义:先遍历完与初始状态最近的状态,然后再遍历与遍历完状态的最近的状态。从开始状态–>只需一次转移就到达的状态–>只需两次就到达的状态–>……..

如上一篇文章的二叉树

                1           2          3        4    5     6     7       8 9 10 11 12 13 14 15

如果按照BFS如何遍历呢?
(1)从1开始遍历;(2)遍历与1最近的结点,即2和3;(3)遍历与2和3最近的结点,即4,5,6,7;(4)遍历与4,5,6,7最近的结点,即8,9,10,11,12,13,14,15。完成。


如何实现BFS?

答:队列。试想一下队列的特点——FIFO(先进先出),ok,那么我们模拟一下结点入队出队的过程。
(1)创建一个空队列q;
(2)将结点1入队;队列状态为:1
(3)循环(下面的步骤都在这个循环里完成),条件是判断队列q是否为空,因为如果所有结点都不再入队即遍历完成;
(4)取队头结点赋给新结点V,然后出队,即结点1出队,目前队列为空,结点V就是结点1;队列状态为:NULL
(5)找到与V相邻的所有结点,即结点2和3,然后入队;队列状态为:2 3
(6)取队头元素,即结点2,赋给V,然后出队;队列状态为:3
(7)找到与V相邻的所有结点,即结点4和5,然后入队:队列状态为:3 4 5
(8)。。。。。不再赘述过程

基本过程就是这样,求最短路(无权)的思路也是如此,每个结点有个dis值,表示从头结点开始到其中一个结点的最短距离。过程和上面基本一致,就是需要将所有结点的dis值先初始化为0,然后遍历到一个结点,就将此结点的dis等于它的父节点的dis+1。


-例题.树的层次遍历-UVA122

算法入门经典上给出一个例题,我们来分析一下。

问题描述:输入一个二叉树,然后从上到下从左到右的顺序输出各个结点的值。每个结点都按照从根结点到它的移动序列给出(L表示左,R表示右)。
样例输入:(11,LL) (7,LLL) (8,R) (5,) (4,L) (13,RL) (2,LLR) (1,RRR) (4,RR) ()
样例输出:5 4 8 11 13 4 7 2 1

这个题目的bfs就是按照我刚才整理的思路用队列来实现,其实我觉得难度在于如何按照题目要求进行输入,将结点保存起来。按照刘汝佳老师的代码进行分析。

#include<cstdio>#include<cstring> #include<queue>#include<vector>using namespace std;const int maxn = 256+10;struct Node{    bool have_value;    int v;//用来保存结点值     Node *left,*right;    Node():have_value(false),left(NULL),right(NULL){}};//定义一个结点结构体 Node* newnode(){    return new Node();}//创建新结点函数 char s[maxn];Node* root;bool failed;void addnode(int v,char* s){    int n = strlen(s);    Node* u = root;    for(int i=0;i<n;i++)    {        if(s[i]=='L')        {            if(u->left==NULL) u->left = newnode();//如果结点不存在,建立新结点             u = u->left;//往左走到达新建立的结点         }        else if(s[i]=='R')        {            if(u->right==NULL) u->right = newnode();            u = u->right;        }    }    if(u->have_value) failed = true;//已经赋值过,表明输入有误     u->v = v;    u->have_value = true;//做标记 }bool read_input(){    failed = false;    root = newnode();    for(;;)    {        if(scanf("%s",s)!=1) return false;        if(!strcmp(s,"()")) break;//如果输入为"()",即输入结束,直接退出循环。        int v;        sscanf(&s[1],"%d",&v);//将字符串中的结点值读取出来赋给V        addnode(v,strchr(s,',')+1);//strchr函数是返回,首次出现的位置,然后插入结点     }       return true; }bool bfs(vector<int>& ans){    queue<Node*> q;    ans.clear();//每次遍历之前要先重置vector,即清空     q.push(root);//将根结点先放入队列中     while(!q.empty())//判断是否为空     {        Node* u = q.front();q.pop();//取队头结点赋给u,并出队         if(!u->have_value) return false;//如果有结点没有赋值,则输入有误         ans.push_back(u->v);//将当期结点的值插入到向量中         if(u->left!=NULL)         {            q.push(u->left);//如果左子结点不为空,则将左子结点入队            }        if(u->right!=NULL)        {            q.push(u->right);        }    }    return true;} int main(){    vector<int> v;    while(read_input())    {        if(!bfs(v)) failed = 1;        if(failed) printf("not complete\n");        else{            for(int i=0;i<v.size();i++)             {            if(i != 0) printf(" ");            printf("%d", v[i]);            }            printf("\n");        }     }     return 0;}

这题价值还是非常大的,通过vector来保存遍历的结点值,一边遍历一边保存。

1 0