搜索与图论-DFS

简介: 搜索与图论-DFS

文章目录

一、DFS

1. DFS 简介

2. DFS 的实现步骤

3. DFS 实际演示

二、DFS 例题——排列数字

具体实现

1. 样例演示

2. 实现思路

3. 代码注解

4. 实现代码

三、DFS 例题—— n-皇后问题(经典)

具体实现——按行进行枚举

1. 样例演示

2. 实现思路

3. 代码注解

4. 实现代码

具体实现——按格子进行枚举

1. 实现思路

2. 实现代码


一、DFS

  • DFS 的关键点是递归和回溯。

1. DFS 简介


DFS(Depth First Search)是深度优先遍历,是图论当中一种非常重要的算法,生产上广泛用于拓扑排序,寻路(走迷宫),搜索引擎,爬虫等。

其定义是,不断地沿着顶点的深度方向遍历。顶点的深度方向是指它的邻接点方向。

主要用于解决是否存在一个我们所需要的结果。因为 DFS 会首先把一种可能的情况尝试到底。才会回溯去尝试下一种情况,只要找到一种情况,就可以返回了。

DFS 问题一般分为两类:

(1) 定义的 DFS :对图的连通性进行测试,典型的问题:迷宫连通性测试、图的条件搜索等。

(2) 广义的 DFS–DFS 思路的应用: DFS 搜索顺序+规则问题、穷举结果寻求最优解/符合条件解等等,由于其穷举答案的本质,又被称为爆搜。


2. DFS 的实现步骤


  • (1) 从顶点出发。
  • (2) 访问顶点,也就是根节点。
  • (3) 依次从顶点的未被访问的邻接点出发,进行深度优先遍历;直至和顶点有路径相通的顶点都被访问。
  • (4) 若此时尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到所有顶点均被访问过为止。


3. DFS 实际演示

0c5a99cd356f4f2483dac6a52b180526.png



(1) 首从根节点 1 开始遍历,它相邻的节点有 2,3,4,先遍历节点 2,再遍历 2 的子节点 5,然后再遍历 5 的子节点 9。


35bcd7e809c4444c9fef3020b7cfd7f7.png


2) 上图中一条路已经走到底了( 9是叶子节点,再无可遍历的节点 ),此时就从 9 回退到上一个节点 5,看下节点 5 是否还有除 9 以外的节点,没有继续回退到 2,2 也没有除 5 以外的节点,回退到 1,1 有除 2 以外的节点 3,所以从节点 3 开始进行深度优先遍历,如下。


05c94f82e7e04325ad18169ee0dd4ce5.png


(3) 同理从 10 开始往上回溯到 6, 6 没有除 10 以外的子节点,再往上回溯,发现 3 有除 6 以外的子点 7,所以此时会遍历 7。

2d97d8e0ca694bfd98d13849523a4b63.png

  • (4) 从 7 往上回溯到 3, 1,发现 1 还有节点 4 未遍历,所以此时沿着 4, 8 进行遍历,这样就遍历完成了。
  • (5) 完整的节点的遍历顺序如下(节点上的的蓝色数字代表遍历的顺序)。


2b37ddea70d44a46ab48faa2d3edfb16.png



二、DFS 例题——排列数字

题目描述

给定一个整数 n,将数字 1∼n 排成一排,将会有很多种排列方法。

现在,请你按照字典序将所有的排列方法输出。

输入格式


共一行,包含一个整数 n。

输出格式

按字典序输出所有排列方案,每个方案占一行。

数据范围

1 ≤ n ≤ 7

输入样例


3

font size=5> 输出样例

1 2 3

1 3 2

2 1 3

2 3 1

3 1 2

3 2 1



具体实现

1. 样例演示



题目当中要求的是将数字 1∼n 排成一排,枚举每一种可能发送的情况。

最开始的时候,三个位置都是空的 _ _ _ 。

首先,填写第一个空位,第一个空位可以填 1 _ _ 。

填好第一个空位,填第二个空位,第二个空位可以填 2,填写后为:1 2 _ 。

填好第二个空位,填第三个空位,第三个空位可以填 3,填写后为: 1 2 3 。

此时,空位填完,无法继续填数,因此,这是第一种方案,输出该方案,然后进行回溯。

退到了状态:1 2 _ 。剩余第三个空位没有填数。第三个空位上除了填过的 3 ,没有其他数字可以填。


因此再往后退一步,退到了状态:1 _ _ 。第二个空位上除了填过的 2,还可以填 3 。第二个空位上填写 3,填写后为:1 3 _ 。

填好第二个空位,填第三个空位,第三个空位可以填 2,填写后为:1 3 2 。

这时候,空位填完,无法继续填数,所以这是第二种方案,输出该方案,然后进行回溯。

然后往后退一步,退到了状态:1 3 _ 。剩余第三个空位没有填数。第三个空位上除了填过的 2,没有其他数字可以填。

因此再往后退一步,退到了状态:1 _ _。第二个空位上除了填过的 2,3,没有其他数字可以填。


  • 因此再往后退一步,退到了状态:_ _ _。第一个空位上除了填过的 1,还可以填 2 和 3 。
  • 2 和 3 的填写方法跟 1 的填写方法相同,在此不做过多叙述,大家可以进行类比思考。
  • 整体流程如下图所示。


4dc38a17cf9841cf827a83900b7f3601.png


2. 实现思路

  • 详见样例演示。


3. 代码注解


st[N] 数组表示数字是否被用过,用 bool 类型对其进行表示,当其为 true 时表示被使用过,当其为 false 时,表示其没有被使用。

path[N] 数组保存排列。当排列的长度为 n 时,表明所有位置均已填充,为一种方案,对其进行输出。

dfs(i) 表示的含义是在 path[i] 处填写数字,然后递归的在下一个位置填写数字。

回溯是指第 i 个位置填写某个数字的所有情况都遍历后, 第 i 个位置填写下一个数字。


在完成以某一个数字为第一位置所有情况的输出时,要进行回溯,然后要注意将当下情况恢复为原始情况,即 state[N] 变为 false ,尚未使用。在此处,path[i] 不需要进行恢复原样,因为 path[i] 会被覆盖。


4. 实现代码

#include <bits/stdc++.h>
using namespace std;
const int N = 10;
int n;
int path[N];
bool st[N];
void dfs(int u)
{
    if (u == n)
    {
        for (int i = 0; i < n; i ++ )
        {
            cout << path[i] << ' ' ;
        }
        cout << endl;
        return;
    }
    for (int i = 1; i <= n; i ++)
    {
        if (!st[i])  //判断这个数是否被使用过
        {
            path[u] = i;
            st[i] = true;
            dfs(u + 1);
            //path[i]不需要进行恢复原样,因为path[i]都会被覆盖。
            st[i] = false;
        }
    }
}
int main()
{
    cin >> n;
    dfs(0);
    system("pause");
    return 0;
}



三、DFS 例题—— n-皇后问题(经典)

题目描述


n − 皇后问题是指将 n 个皇后放在 n×n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。


4d2bcffc1a8a4b78b53cc3cdb2697485.png



现在给定整数 n,请你输出所有的满足条件的棋子摆法。


输入格式

共一行,包含整数 n。


输出格式

每个解决方案占 n 行,每行输出一个长度为 n 的字符串,用来表示完整的棋盘状态。

其中 . 表示某一个位置的方格状态为空,Q 表示某一个位置的方格上摆着皇后。

每个方案输出完成后,输出一个空行。


注意:行末不能有多余空格。

输出方案的顺序任意,只要不重复且没有遗漏即可。

数据范围

1 ≤ n ≤ 9

输入样例

4

输出样例


.Q…

…Q

Q…

…Q.

…Q.

Q…

…Q

.Q…

具体实现——按行进行枚举

1. 样例演示

  • 具体样例演示如下图:

04b322554f33407cbeac1e2bf47a6b5e.png

2. 实现思路


  • 首先,从第一行开始看,皇后可以放到哪一列。
  • 然后每一行依次进行枚举。
  • 注意:剪枝,就是提前判断,当前这个方案一定不合法时,直接将与这个方法相关的都完全排除掉。


3. 代码注解


ba036845c14441f3b956a03502816b4b.png


  • 因为 y-x 可能会是负数,但我们的数组下标不能是负数,所以,在此我们加上偏移量 n ,保证数组下标均为正数。

4. 实现代码


#include <bits/stdc++.h>
using namespace std;
const int N = 20;
int n;
char g[N][N];
bool col[N],dg[N],udg[N];//列,对角线,反对角线
void dfs(int u)
{
    if (u == n)
    {
        for (int i = 0; i < n; i ++ )
        {
            puts(g[i]);
        }
        puts("");
        return;
    }
    for (int i = 0; i < n; i ++)
    {
        if (!col[i] && !dg[u + i] && !udg[n - u + i]) 
        {
            g[u][i] = 'Q';
            col[i] = dg[u + i] = udg[n - u + i] = true;
            dfs(u + 1);
            col[i] = dg[u + i] = udg[n - u + i] = false;
            g[u][i] = '.';
        }
    }
}
int main()
{
    cin >> n;
    for(int i = 0; i < n; i ++)
    {
        for(int j = 0;j < n; j ++)
        {
            g[i][j] = '.';
        }
    }
    dfs(0);
    system("pause");
    return 0;
}


具体实现——按格子进行枚举

1. 实现思路


对每个格子进行枚举,每个格子都有放或不放两种情况,只要我们枚举到最后一个格子,就可以知道共有几种布局方案了。


cd593104ae7d499daf63cfd770d5e4ed.png



2. 实现代码

#include <bits/stdc++.h>
using namespace std;
const int N = 20;
int n;
char g[N][N];
bool row[N], col[N], dg[N * 2], udg[N * 2];
void dfs(int x,int y,int s)//x,y 表示坐标,u表示当前有几个皇后
{
    if (s > n) 
    {
        return;
    }
    if (y == n)
    {
        y = 0;
        x ++ ;
    }
    if (x == n)
    {
        if (s == n)
        {
            for (int i = 0; i < n; i ++ ) 
            {
                puts(g[i]);
            }
            puts("");
        }
        return;
    }
//不放皇后
    g[x][y] = '.';
    dfs(x, y + 1, s);
//放皇后
    if (!row[x] && !col[y] && !dg[x + y] && !udg[x - y + n])
    {
        row[x] = col[y] = dg[x + y] = udg[x - y + n] = true;
        g[x][y] = 'Q';
        dfs(x, y + 1, s + 1);
        g[x][y] = '.';
        row[x] = col[y] = dg[x + y] = udg[x - y + n] = false;
    }
}
int main()
{
    cin >> n;
    for(int i = 0; i < n; i ++)
    {
        for(int j = 0;j < n; j ++)
        {
            g[i][j] = '.';
        }
    }
    dfs(0,0,0);
    system("pause");
    return 0;
}
















相关文章
|
3月前
|
存储 算法 Java
广度优先搜索(Breadth-First Search,BFS)是一种用于图的遍历或搜索的算法。
广度优先搜索(Breadth-First Search,BFS)是一种用于图的遍历或搜索的算法。
|
3月前
|
算法 Python
蓝桥杯-搜索BFS+DFS
蓝桥杯-搜索BFS+DFS
27 2
|
4月前
|
存储 机器学习/深度学习 算法
搜索(DFS与BFS):
搜索(DFS与BFS):
40 0
搜索(DFS与BFS):
|
10月前
|
C++
DFS之连通性问题+搜索顺序
DFS之连通性问题+搜索顺序
40 0
|
机器学习/深度学习 算法
搜索与图论 - 搜索与图在算法中的应用【中】
搜索与图论 - 搜索与图在算法中的应用【中】
|
存储 机器学习/深度学习 算法
搜索与图论 - 搜索与图在算法中的应用【上】
搜索与图论 - 搜索与图在算法中的应用【上】
|
算法 Python
基于python实现深度优先遍历搜索(DFS)
基于python实现深度优先遍历搜索(DFS)
397 0
基于python实现深度优先遍历搜索(DFS)
|
存储 机器学习/深度学习 算法
搜索与图论-BFS
搜索与图论-BFS
|
存储 算法
搜索与图论 - spfa 算法
搜索与图论 - spfa 算法