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

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

bfs和dfs都是遍历图的方法。dfs是不撞南墙不回头,bfs慢慢来,一层一层来。

类型空间(h为高度)时间(h为高度)采用的数据结构特点
DFSO(h)O(2h2^h2h)不具备最短路
BFSO(2h2^h2h)O(2h2^h2h)队列“最短路”

说明

  • bfs只能做权重为1或者相同的 “最短路”
  • 空间上:
    • dfs 复杂度为最大深度 h。这也是stack最大容量。
    • bfs 复杂度为 2^h。因为满二叉树 最下面一层为 2^(h-1)个节点。 这是个queue最大容量。
  • 时间上:
    • 不优化的情况下,时间都是全部搜完所有的点,所以是O(2h2^h2h)的时间复杂度

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

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

算法思路

  • 一层层搜索
  • 将一个点所有邻居加入队列中。
  • 然后将列队中一个点,所有邻居加入队列。重复这个过程。
  • 需要判重。加入过的。不再加入了。

模板

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);
        }
    }
}

例题

迷宫

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

image-20211101232449908

想法

  • 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-239SoKKj-1642219171361)(D:/坚果同步文件夹/我的坚果云/算法刷题笔记/图库/1590211824694.png)]

算法思路

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

例题

数字的全排列

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

1589868115039

#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;
}

N皇后

  • dg代表正对角线,udg代表反对角线是否有占的

默认每一行只放一个。 那么dfs只用考虑列的情况。

image-20211101175554987

//AcWing 843. n-皇后问题
// Created by majoe on 2020/5/20.
//https://www.acwing.com/activity/content/punch_the_clock/11/

#include <bits/stdc++.h>
using namespace std;
const int N = 20;
int n,dg[N],udg[N];//dg正对角,udg反对角
char m[N][N];
int col[N];//表示是否列被占住了
//走到下标为u的行
void dfs(int u){
   if( u == n) { // 边界条件
       for (int i = 0; i < n; i ++ ) puts(m[i]);
       puts("");
       return;
   }

   for (int i = 0; i < n; ++i) {
       //利用斜率求出 正对角和斜对焦
       if(!col[i] && !dg[i+u] && !udg[n + u - i]){
           m[u][i] = 'Q';
           col[i] = dg[i+u]= udg[n + u - i] = 1;
           dfs(u+1);
           col[i] = dg[i+u]= udg[n + u - i] = 0;
           m[u][i] = '.';
       }
   }

}
int main(){
   cin >> n;
   for (int i = 0; i < n; i ++ )
       for (int j = 0; j < n; j ++ )
           m[i][j] = '.';

   dfs(0);
   return 0;
}

树的重心

题意

1590233175578

1590233157386

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

1590236944675

1589875088045

实现
#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值