DFS和BFS 原理探究和比较

深度优先搜索和宽度优先搜索

bfs和dfs都是遍历图的方法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oPE5oqDt-1591066356009)(../../图库/1590211529631.png)]

说明

  • bfs只能做权重为1或者相同的 “最短路”
  • 空间上
    • dfs 复杂度为最大深度 h。这也是stack最大容量。
    • bfs 复杂度为 2^h。因为满二叉树 最下面一层为 2^(h-1)个节点。 这是个queue最大容量。

bfs——广度优先算法(暴搜)

  • 适合解决最小步数问题。(bfs只能做权重为1或者相同的 “最短路”)
  • 比dfs用时间更短
  • dfs是穷举。到一个点需要3步,那么bfs会在第三层就找到。

算法思路

  • 一层层搜索

  • 将一个点所有邻居加入队列中。

  • 然后将列队中一个点,所有邻居加入队列。重复这个过程。

  • 需要判重。加入过的。不再加入了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gG6ClO1O-1591066356013)(../../图库/1590211235849.png)]

模板

queue<int> q;
st[1] = true; // 表示1号点已经被遍历过
q.push(1);

while (q.size())
{
    int t = q.front();
    q.pop();

    for (int i = h[t]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j])
        {
            st[j] = true; // 表示点j已经被遍历过
            q.push(j);
        }
    }
}

例题

迷宫

从左上到右下角。最少的步数。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qZ7hcSQx-1591066356017)(../../图库/1590211035398.png)]

想法

  • bfs + 记忆化

  • d[i][j]代表(00)到(i,j)最短距离
    
  • bfs第一次搜索到的右下角的点。d数组的值就是答案。

// AcWing 844. 走迷宫
// Created by majoe on 2020/5/20.
//https://www.acwing.com/activity/content/problem/content/907/1/

#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int n ,m;
int mi[N][N],d[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

int bfs(){
    queue<int> h;
    queue<int> l;
    //h代表行,l代表列。
    h.push(1);
    l.push(1);

    memset(d,-1, sizeof(d));
    d[1][1] = 0; // (1,1)到(1,1)距离为0
    //不为空
    while (!h.empty()){
        int a = h.front(), b = l.front();
        h.pop(); l.pop();
        for (int i = 0; i < 4; ++i) {
            int xx = a + dx[i];
            int yy = b +dy[i];
            if(xx>=1 && yy >=1 &&  xx <= n && yy <= m  && mi[xx][yy] == 0 && d[xx][yy] == -1){
                //d[xx][yy] = 1表示xx,yy没被访问过
                h.push(xx);l.push(yy);
                d[xx][yy] = d[a][b]+1;
            }
        }
    }
    return d[n][m];
}

int main(){
    cin >> n >> m;
    for (int i = 1; i <= n ; ++i) {
        for (int j = 1; j <= m; ++j) {
            cin >> mi[i][j];
        }
    }
    int ans = bfs();
    cout << ans;
    return 0;
}

深度优先搜索 - dfs

特点

  • 每次搜索都会到头
  • 一般dfs遍历图。用邻接表比较好。时间复杂度低.
  • 并且如果没有路的话,会回溯。也就是“回头”。
  • 利用stack。进行回溯
    • 回溯需要恢复现场
  • 同时需要有一个vis数组,将用过的点。记录下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RHuUnzJr-1591066356023)(../../图库/1590211824694.png)]

算法思路

   void dfs(int step){//变量据题而命
    if(判断边界){
        ...
    }
    //尝试每种可能;
    for(int i=1;i<=n;i++){
        //保证没有被访问过
        if(vis[step]){
         vis[step] = true;
        dfs(step+1);//继续下一步
        vis[step] = false; // 回溯
        }
    }
    return;
}

一般dfs遍历图。用邻接表比较好。时间复杂度低.

模板

int dfs(int u)
{
    st[u] = true; // st[u] 表示点u已经被遍历过

    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j]) dfs(j);
    }
}

例题

数字的全排列

1-n数字的全排列。n=3时。

#include <iostream>
using namespace std;
const int N = 10;

int path[N];
bool st[N];
int n;

// 计算u->n的所经过的路径path
void dfs(int u) 
{
    // 边界条件
    if (u == n) 
    {
        for (int i = 0; i < n; i++) printf("%d ", path[i]);
        puts("");
        return;
    }
    else 
    {
        for (int i = 1; i <= n; i++) 
        {
            if (!st[i]) 
            {
                path[u] = i;
                st[i] = true;
                dfs(u+1); // 回溯之后, 
                 
                st[i] = false;
            }
        }
    }
}

int main()
{
    scanf("%d", &n);
    dfs(0);
    return 0;
}

树的重心

题意

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D9WFALvq-1591066356037)(../../图库/1590233175578.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uxkgVN53-1591066356038)(../../图库/1590233157386.png)]

想法
  • dfs
  • 利用dfs遍历每一个节点。dfs()返回该节点下包含多少个节点。记做sum
  • 当然,dfs(u)时,会切分子节点为若干个子树。记录子树的包含的最大节点数为 res
  • 那么 max(res,n - sum) 的最小值就是 答案。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f8wefJcX-1591066356041)(../../图库/1590236944675.png)]

实现
//
//
#include <bits/stdc++.h>
using namespace std;

const int N = 2e5 +10; //无向图,容量应该是两倍
int n;//n个节点
int h[N],ne[N],e[N],idx;
int ans = 1e9;
int vis[N];

void add(int a, int b){
    e[idx] = b; ne[idx] = h[a];h[a] = idx++;
}

//得到以U为顶点,包含的点的个数
int dfs(int u){
    vis[u] = 1;//本次点只能遍历一次。所以不用回溯

    int sum = 1,res=0;// res表示u节点某一节点包含节点的个数
    //遍历u的直接边
    for (int i = h[u]; i != -1 ; i = ne[i]) {
        int j = e[i];//j为u指向的点
        if (vis[j]) continue;
        int s = dfs(j);
        res = max(res,s);
        sum += s; // sum表示 u 的包含的总结点树
    }
    res = max(res,n-sum);
    ans = min(ans,res);
    return sum;
}


int main(){

    cin >> n;
    memset(h,-1 , sizeof(h));
    for (int i = 0; i < n - 1; ++i) {
        //n-1条边
        int a,b;
        cin >> a >> b;
        add(a,b);
        add(b,a);//无向图
    }
    dfs(1);//遍历第一个点
    cout << ans << endl;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值