深度优先搜索和广度优先搜索(基础自学)(c++实现)

DFS与BFS对比

我们假设一个节点衍生出来的相邻节点平均的个数是N个,那么当起点开始搜索的时候,队列有一个节点,当起点拿出来后,把它相邻的节点放进去,那么队列就有N个节点,当下一层的搜索中再加入元素到队列的时候,节点数达到了N2,你可以想想,一旦N是一个比较大的数的时候,这个树的层次又比较深,那这个队列就得需要很大的内存空间了。

于是广度优先搜索的缺点出来了:在树的层次较深&子节点数较多的情况下,消耗内存十分严重。广度优先搜索适用于节点的子节点数量不多,并且树的层次不会太深的情况。

那么深度优先就可以克服这个缺点,因为每次搜的过程,每一层只需维护一个节点。但回过头想想,广度优先能够找到最短路径,那深度优先能否找到呢?深度优先的方法是一条路走到黑,那显然无法知道这条路是不是最短的,所以你还得继续走别的路去判断是否是最短路?

于是深度优先搜索的缺点也出来了:**难以寻找最优解,仅仅只能寻找有解。**其优点就是内存消耗小,克服了刚刚说的广度优先搜索的缺点。
参考

深度优先搜索(DFS)

入门例题一: 水池的个数

原博客

输入
第一行输入一个整数N,表示共有N组测试数据
每一组数据都是先输入该地图的行数m(0<m<100)与列数n(0<n<100),
然后,输入接下来的m行每行输入n个数,表示此处有水还是没水
(1表示此处是水池,0表示此处是地面)

输出
输出该地图中水池的个数。
每个水池的旁边(上下左右四个位置)如果还是水池的话的话,它们可以看做是同一个水池。

样例输入
2
3 4
1 0 0 0
0 0 1 1
1 1 1 0
5 5
1 1 1 1 0
0 0 1 0 1
0 0 0 0 0
1 1 1 0 0
0 0 1 1 1

样例输出
2
3

这是一道DFS的题目,简单来说就是不撞南墙不回头。用DFS对每个点的上下左右搜索,如果有水(==1)那么这个地方标记为0。

一趟搜索下来,我们可以把跟这个点连接的水池全部标记为0,然后给计数器变量加1,代表这整个为1个水池。

只要对所有点都进行一遍dfs,则可以求出所有水池数目。

#include <bits/stdc++.h>
#define N 105
using namespace std;
int arr[N][N];

void dfs(int a,int b)
{
    if(arr[a-1][b]==1){arr[a-1][b]=0,dfs(a-1,b);}  //向上搜索
    if(arr[a+1][b]==1){arr[a+1][b]=0,dfs(a+1,b);}  //向下搜索
    if(arr[a][b-1]==1){arr[a][b-1]=0,dfs(a,b-1);}  //向左搜索
    if(arr[a][b+1]==1){arr[a][b+1]=0,dfs(a,b+1);}  //向右搜索
}
int main(void)  // 传入void
{
    int t;
    int n,m;
    cin>>t;
    while(t--)
    {
        int cnt=0;
        cin>>n>>m;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                cin>>arr[i][j];

        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            {
                if(arr[i][j]==1)   //代表此点为水池
                {
                    cnt++;
                    dfs(i,j);     //将与该水池相邻的水池都标记为0
                }
            }

            cout<<cnt<<endl;
    }

    return 0;
}

分析:
例如输入:
3 4
1 0 0 0
0 0 1 1
1 1 1 0

当i=1,j=1的时候,有一个水池(==1),那么进入if语句,计数器加一,然后开始搜索i=1,j=1周边有没有水池(结果是没有),所以继续进行for循环。

当到i=2,j=3的时候,又有一个水池,进入if,计数器此时为2了,开始搜索i=2,j=3周边,结果右边和下面都有,然后把右边和下面标为0(防止之后主函数的for循环==1中计数器再加,因为连接的水池都看作一个)然后开始搜索那个右边和下面的,之后同理。

可以发现,这样搜索的话,只要旁边有水池,dfs()函数就可以把它槟城0,这样在下面的循环中,计数器就不会再加了,达到了连接的水池都看作一个的目的。

入门例题二:N皇后问题

链接在这
(下文都是参考上面链接滴)

在一个 N × N 的棋盘上放置 N 个皇后,每行刚好放置一个并使其不相互攻击(同一行、同一列、同一斜线上的皇后都是自动攻击),求一共有多少种合法的方法放置 N 个皇后 。
输入格式:输入一个整数N,代表N个皇后。
输出格式:输出一个整数,表示有多少种放置皇后方法。
在这里插入图片描述

设置三个数组,一个用来储存列(只需要遍历行,不需要遍历列,否则会出现行和列都同时存在皇后的情况)两个用来储存对角线。
在这里插入图片描述

于是,对于对角线1(从右上到左下),我们可以表示为 (行 + 列);
同理,对于对角线2(从左上到右下)可以表示为(行 - 列 + n)。

代码如下:

//N皇后
#include<bits/stdc++.h>
using namespace std;
int n, ans;
bool vy[1005], vd1[1005], vd2[1005];
bool check(int x, int i)
{
	return !vy[i] && !vd1[x + i] && !vd2[x - i + n];
}    //检查这个地方能不能放的函数,如果三个地方都是False(三个地方都能放),那么return true
void dfs(int x)
{
	if(x == n)
	{
		//从0行开始的,所以当 x = n 时候就结束 
		ans++;
		return; 
	}
	for(int i = 0; i < n; i++)
	{
		//每一行内,对列情况讨论(每一个小 path) 
		if(check(x, i))
		{
			//如果可以放置
			vy[i] = true; 
			vd1[x + i] = true;
			vd2[x - i + n] = true;
			dfs(x + 1); //进行下一行搜素
			//搜索完成后需要释放掉 
			vd1[x + i] = false;
			vd2[x - i + n] = false;
			vy[i] = false; 
		}
	}
}
int main()
{
	cin>>n;
	dfs(0);
	cout<<ans;
	return 0;
} 

待补,真的不是很懂TT

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
深度优先搜索广度优先搜索是常见的图遍历算法,以下是它们的C++代码实现深度优先搜索: ``` #include<bits/stdc++.h> using namespace std; const int N = 10010; vector<int> g[N]; // 存储图 bool vis[N]; // 标记是否已经访问 void dfs(int u) { vis[u] = true; // 标记已经访问 printf("%d ", u); // 输出当前节点 for (int i = 0; i < g[u].size(); i++) { // 遍历所有邻接节点 int v = g[u][i]; if (!vis[v]) { // 如果未访问过,递归访问 dfs(v); } } } int main() { int n, m; scanf("%d%d", &n, &m); for (int i = 0; i < m; i++) { int u, v; scanf("%d%d", &u, &v); g[u].push_back(v); // 添加边 g[v].push_back(u); } for (int i = 1; i <= n; i++) { if (!vis[i]) { // 如果未访问过,从该节点开始进行dfs dfs(i); } } return 0; } ``` 广度优先搜索: ``` #include<bits/stdc++.h> using namespace std; const int N = 10010; vector<int> g[N]; // 存储图 bool vis[N]; // 标记是否已经访问 void bfs(int u) { queue<int> q; q.push(u); // 将起点入队 vis[u] = true; // 标记已经访问 while (!q.empty()) { // 当队列不为空时进行循环 int t = q.front(); q.pop(); printf("%d ", t); // 输出当前节点 for (int i = 0; i < g[t].size(); i++) { // 遍历所有邻接节点 int v = g[t][i]; if (!vis[v]) { // 如果未访问过,标记已经访问并入队 vis[v] = true; q.push(v); } } } } int main() { int n, m; scanf("%d%d", &n, &m); for (int i = 0; i < m; i++) { int u, v; scanf("%d%d", &u, &v); g[u].push_back(v); // 添加边 g[v].push_back(u); } for (int i = 1; i <= n; i++) { if (!vis[i]) { // 如果未访问过,从该节点开始进行bfs bfs(i); } } return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值