【讲解 + 模板】广度优先搜索算法BFS

广度优先搜索算法BFS

广度优先搜索算法(又称宽度优先搜索)(简称BFS、八法师)是最简便的图的搜索算法之一,这一算法也是很多重要的图的搜索算法的原型。其实bfs不仅可以实现图的搜索问题,也可以实现很多最优解问题等。
bfs的核心实现方法就是队列(STL与手写均可),核心思想为按规则逐层产生搜索树(注意:bfs是逐层产生,而dfs是不断产生新层数直到到达最底层开始回溯),下面开始深入介绍。
这里写图片描述

基本模型

(伪代码)
int bfs()
{
初始化,初始状态存入队列;
队列首指针head=0;尾指针tail=1;
do
  {
    指针head后移一位,指向待扩展节点;
    for(int i = 1; i <= max; i++)
      {
        if(子结点符合条件)
          {
            tail指针加1,把新结点存入队尾;
            if(新结点与原已产生节点重复)
              删去该节点(取消入队,tail减1);
            else
              if(新结点是目标节点)  输出并退出;
          }
        }
      }while(head < tail);
    }

双向BFS

和普通bfs不同的是,双向bfs同时存入了初始与结束两个状态,利用两个队列,按照相同或相反的扩展规则同时在初始与结束两端进行正反向bfs。若一个方向(方向1)的bfs产生的状态在另一个方向(方向2)扩展时曾经出现过,则代表搜索到了结果,检查这个状态在方向2上搜索时何时(或在何层)产生过,与方向1上搜索的结果按规则合并(通常是搜索层数相加),得出的结果即为搜索最终结果。举例说明:若此时搜索层数为5层(正向反向都为5层),在正向搜索树上产生了反向搜索树上曾产生状态,则表示已找到答案,终止搜索;通过枚举假设发现该状态在反向搜索树的第3层上出现,则由初始状态转移到终止状态所需的最少步数为5 + 3 = 8步。

  1. 优点:相比于普通的bfs,极大地节省了时间和空间,尤其是空间(极大减少了扩展产生的结点数,bfs新结点的产生个数是指数型增长的,指数爆炸啊QAQ)
    这里写图片描述
  2. 使用限制:只有通过从终止状态开始通过相同或相反的扩展规则可以反向搜索出初始状态的题目才可以使用;且双向bfs产生的状态通常需要进行状态压缩,需要把状态压缩成方便进行状态判重与检索枚举的整数型状态(例如当前状态为一个坐标(56,71,28),可以把状态压缩成567128利用set,map,bool数组或hash等进行状态判重与检索,而在进行bfs状态转移时再把567128还原成(56,71,28))。
  3. 典型例题:八数码难题,这个题是一道非常经典的bfs,利用优化bfs或双向bfs都可以做,建议大家试试。
  4. 补充:双向bfs在实现时,写好正向bfs,然后直接复制,该一下变量及扩展规则就可以变成反向bfs,逐层双向扩展与逐节点双向扩展均可。
    逐层扩展:
void bfs()
{
    for(int i = 1; i <= max; i++)
      {
        正向bfs算式;
        if(产生状态在反向曾产生过)  输出结果,结束程序;
      }
    for(int i = 1; i <= max; i++
      {
        反向bfs算式;
        if(产生状态在正向曾产生过)  输出结果,结束程序;
      }
}

逐节点扩展:

void bfs()
{
    for(int i = 1; i <= max; i++)
      {
        正向bfs算式;
        if(产生状态在反向曾产生过)  输出结果,结束程序;
        反向bfs算式;
        if(产生状态在正向曾产生过)  输出结果,结束程序;
      }
}

关于怎样记录扩展层数

这里写图片描述
注意扩展层数与扩展次数不同,最简单的方法就是建立一个包括数据与扩展层数结构体,然后在bfs时记录即可,代码如下

(伪代码)
struct node
{
    int data; //数据
    int step; //扩展层数
}q[10000];
int head = 0, tail = 1;
q[1].data = 数据;
q[1].step = 0;
int linshi;

void bfs()
{
    do
      {
        head++;
        for(int i = 1; i <= 算符种数; i++)
          {
            linshi = q[head].data + dx[i] //dx为扩展规则;
            if(linshi满足条件)
              {
                tail++;
                q[tail].data = linshi;
                q[tail].step = q[head].step + 1; //扩展层数+1
              }
            if(产生状态重复)  tail--;  //取消入队(这一步也可以加在判断linshi是否符合条件处,直接不让linshi入队即可)
            if(找到结果)
              {
                输出并结束程序(exit(0))或退出bfs(return);
              }
          }
      }while(head <= tail)
}

经典例题

1.八数码难题(单向优化bfs、双向bfs
这里写图片描述

猜你喜欢

转载自blog.csdn.net/Mashiro_ylb/article/details/78261639