广度优先搜索-Part1

引入例题:抓住那头牛

农夫知道一头牛的位置,想要抓住它。农夫和牛都位于数轴上,农夫起始位于点N(0<=N<=100000),牛位于点K(0<=K<=100000)。农夫有两种移动方式:
1、从X移动到X-1或X+1,每次移动花费一分钟
2、从X移动到2*X,每次移动花费一分钟
假设牛没有意识到农夫的行动,站在原地不动。农夫最少要花多少时间才能抓住牛?

场景分析

假设农夫起始位于3,牛位于5,如何搜索一条到5的路径?
在这里插入图片描述

策略一:深度优先搜索:

从起点出发,随机挑选一个方向,能往前走就往前走(扩展),走不动了则回溯。

运气好的话: 3->4->5

运气最坏的话: 3->2->1->0->4->5

如果想要求最优解,需要遍历所有走法,但可以使用一定方法进行剪枝。

运算过程中需要存储路径上的节点,数量较少,用栈存储。

策略二:广度优先搜索

给节点分层。起点就是第0层,从起点最少需要N步就能到达的点就属于第n层。

依层次顺序,从小到大的扩展节点。把层次低的点全部扩展出来后,才会扩展层次高得点。

搜索过程:

3

12 4 6

1 5

注意:扩展时,不能扩展已经走过的节点。可确保找到最优解,但是因扩展出 来的节点较多,且多数节点都需要 保存,因此需要的存储空间较大。 用队列存节点。

广搜算法:

广度优先搜索算法如下:(用QUEUE )

在这里插入图片描述)

  1. 把初始节点S0放入Open表中;
  2. 如果Open表为空,则问题无解,失败 退出;
  3. 把Open表的第一个节点取出放入 Closed表,并记该节点为 n ;
  4. 考察节点 n是否为目标节点。若是, 则得到问题的解,成功退出;
  5. 若节点 n不可扩展,则转第(2)步;
  6. 扩展节点 n,将其不在Closed表和 Open表中的子节点 (判重)放入Open表的尾部 ,并为每一个子节点设置指向父节点的指针 ( 或记录节点的层次),然后转第(2)步

代码实现如下:

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
int N, K;
const int MAXN = 100000;
int visited[MAXN + 10]; //判重标记,为true表示该点已经扩展过了
struct Step
{
    int x;     //位置
    int steps; //到达x需要的步数
    Step(int xx, int ss) : x(xx), steps(ss) {}
};
queue<Step> q; //队列,即open表
int main()
{
    cin >> N >> K;
    memset(visited, 0, sizeof(visited));
    q.push(Step(N, 0)); //把起始位置放入Open表
    while (!q.empty())
    {
        Step s = q.front(); //拿出Open表中第一个元素
        if (s.x == K)
        { //找到目标
            cout << s.steps << endl;
            return 0;
        }
        else
        {
            if (s.x - 1 >= 0 && !visited[s.x - 1])
            {
                q.push(Step(s.x - 1, s.steps + 1));
                visited[s.x - 1] = 1;
            }
            if (s.x + 1 <= MAXN && !visited[s.x + 1])
            {
                q.push(Step(s.x + 1, s.steps + 1));
                visited[s.x + 1] = 1;
            }
            if (s.x * 2 <= MAXN && !visited[s.x * 2])
            {
                q.push(Step(s.x * 2, s.steps + !));
                visited[s.x * 2] = 1;
            }
            q.pop();
        }
    }
    return 0;
}

广搜与深搜的比较

广搜一般用于状态表示比较简单、求最优策略的问题

  • 优点:
    • 是一种完备策略,即只要问题有解,它就一定可以找到解 。并且,
    • 广度优先搜索找到的解,还一定是路径最短的解。
  • 缺点:
  • 盲目性较大,尤其是当目标节点距初始节点较远时,将产 生许多无用的节点,因此其搜索效率较低。
  • 需要保存所有扩展出 的状态,占用的空间大

深搜几乎可以用于任何问题

  • 只需要保存从起始状态到当前状态路径上的节点

双向广度优先搜索(DBFS)

DBFS算法是对BFS算法的一种扩展。

  • BFS算法从起始节点以广度优先的顺序不断扩展,知道遇到目的节点。
  • DBFS算法从两个节点以广度优先的顺序同时扩展,一个是从其实节点开始扩展,一个是从目的节点开始扩展,知道一个扩展队列中出现了另一个队列中已经扩展的节点,就相当于两个扩展的方向有了交点,那么可以认为我们找到了一条路径。

DBFS算法相对于BFS算法来说,由于采用了双向扩展的方式,搜索树的宽度得到了明显的减少,所以在算法的时间复杂度和空间 复杂度上都有较大的优势。

  • 假设1个结点能扩展出n个结点,单向搜索要m层能找到答案,那么扩展出来的节点数目就是: (1- n m n^m nm)/(1-n)
  • 双向广搜,同样是一共扩展m层,假定两边各扩展出m/2层,则总结点数目 2 * (1- n m / 2 n^{m/2} nm/2)/(1-n)
  • 每次扩展结点总是选择结点比较少的那边进行扩展,并不是机械的两边交替.
int expand(i) //其中i为队列的编号,0或1
{
 	取队列qi的头结点H;
 	对H的每一个相邻节点adj:
 		1 如果adj已经在队列qi之中出现过,则抛弃adj;
 		2 如果adj在队列qi中未出现过,则:
 			1) 将adj放入队列qi;
 			2) 如果adj 曾在队列q1-i中出现过, 则:输出找到的路径
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值