bfs dfs

本文通过三个实例详细解析了图论中的DFS(深度优先搜索)与BFS(广度优先搜索)两种算法的应用场景及实现方法,涵盖了算法的基本框架、代码实现及注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

图论

DFS:  从一个分支遍历到结束  沿一条分支一直搜索
实现方式:递归
基本框架:
DFS(int x, int y, int z)
{
for(遍历分支)
dfs(a,b,c)
}

*vector 链式存储   可迅速插入删除


example:

一.变形课

呃......变形课上Harry碰到了一点小麻烦,因为他并不像Hermione那样能够记住所有的咒语而随意的将一个棒球变成刺猬什么的,但是他发现了变形咒语的一个统一规律:如果咒语是以a开头b结尾的一个单词,那么它的作用就恰好是使A物体变成B物体. 
Harry已经将他所会的所有咒语都列成了一个表,他想让你帮忙计算一下他是否能完成老师的作业,将一个B(ball)变成一个M(Mouse),你知道,如果他自己不能完成的话,他就只好向Hermione请教,并且被迫听一大堆好好学习的道理. 
Input
测试数据有多组。每组有多行,每行一个单词,仅包括小写字母,是Harry所会的所有咒语.数字0表示一组输入结束. 
Output
如果Harry可以完成他的作业,就输出"Yes.",否则就输出"No."(不要忽略了句号) 
Sample Input
so
soon
river
goes
them
got
moon
begin
big
0
Sample Output
Yes.


        
  
Harry 可以念这个咒语:"big-got-them".


代码如下

#include <iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
vector<int> e[30];
bool vis[30];
bool flag;
int i;
void dfs(int x)
{
    vis[x] = 1;
    if(x == 12)
    {
        flag = 1;
        return;     //return 可以看作跳出了调用的这个函数,但只是跳出了当前的,要将所有嵌套的都跳出,用exit
    }
        //return true;
    for(auto &v : e[x])
    {
        if(!vis[v])
            dfs(v);
    }
   // return false;
}
int main()
{
    string str;
    while(cin>>str)
    {
        flag = 0;
        memset(vis, 0, sizeof(vis));    //对vis清零  用0替换并返回vis
        for(i = 0; i < 30; i++)     //26个字母
            e[i].clear();       //对容器清零了
        while(str[0] != '0')
        {
            int len = str.length();
            int be = (int)(str[0] - 'a');
            int en = (int)(str[len - 1] - 'a');
            e[be].push_back(en);    //建立对应的关系,一个存一个
            cin>>str;
        }
        dfs(1);
        if(flag)
            printf("Yes.\n");
        else
            printf("No.\n");
    }
    return 0;
}



1.对于这类题,首先要做的事是建图:将题目转化成树杈形状的图,分支一一明确
这道题中,每个单词的首字母和尾字母就是要找的分叉点

最上层的节点为b

第二层的节点是以b为首字母的单词的尾字母

第三层是以上一层尾字母为首字母的单词的尾字母

......

直到找到m为止

2.对于字母,字符这类相对来说难以进行运算的数据类型,将它们转化为整型的数字,来比较某种关系。
我们让a为0, b为1........m为12.........

3.然后就用容器把1中的关系建立起来:e[be].push_back(en);

4.以b为起始点,调用dfs函数

5.dfs函数

   用for(auto &v : e[x])
    {
        if(!vis[v])
            dfs(v);
    }
这种结构遍历e[x]中的元素,此时,x为第一个节点b

    v依次代表了第二层的节点,不断的调用

*其中,为了防止重复调用同一个字母,设置一个判断函数vis

如果x元素进入过dfs函数,就让vis[x] = 1;

只有当vis[x] != 1 的时候才调用。

  直到 x == 12 时,说明已经找到了,停止调用dfs函数,让flag = 1 结束

*这里结束可以直接用return

return 可以看作跳出了调用的这个函数,但只是跳出了当前的,要将所有嵌套的都跳出,用exit

6.还要注意:对于测试多组数据的题目,一定要记得清空数组和容器

清空数组    memset(vis, 0, sizeof(vis));

清空容器    for(i = 0; i < 30; i++)      e[i].clear();     


二.数油田

The GeoSurvComp geologic survey company is responsible for detecting underground oil deposits. GeoSurvComp works with one large rectangular region of land at a time, and creates a grid that divides the land into numerous square plots. It then analyzes each plot separately, using sensing equipment to determine whether or not the plot contains oil. A plot containing oil is called a pocket. If two pockets are adjacent, then they are part of the same oil deposit. Oil deposits can be quite large and may contain numerous pockets. Your job is to determine how many different oil deposits are contained in a grid. 
Input
The input file contains one or more grids. Each grid begins with a line containing m and n, the number of rows and columns in the grid, separated by a single space. If m = 0 it signals the end of the input; otherwise 1 <= m <= 100 and 1 <= n <= 100. Following this are m lines of n characters each (not counting the end-of-line characters). Each character corresponds to one plot, and is either `*', representing the absence of oil, or `@', representing an oil pocket. 
Output
For each grid, output the number of distinct oil deposits. Two different pockets are part of the same oil deposit if they are adjacent horizontally, vertically, or diagonally. An oil deposit will not contain more than 100 pockets. 
Sample Input
1 1
*
3 5
*@*@*
**@**
*@*@*
1 8
@@****@*
5 5 
****@
*@@*@
*@**@
@@@*@
@@**@
0 0 
Sample Output
0
1
2
2

代码如下:

#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int d[8][2] = {-1,1, -1,0, -1,-1, 0,1, 0,-1, 1,1, 1,0, 1,-1};
bool vis[105][105];
char a[105][105];
int m, n;
bool check(int x, int y)
{
    if(x >= 0 && y >= 0 && x < m && y < n && a[x][y] == '@' && !vis[x][y])
        return true;
    return false;
}
void dfs(int x, int y)
{
    int x0, y0;
    vis[x][y] = 1;
    for(int h = 0; h < 8; h++)
    {

        x0 = x + d[h][0];
        y0 = y + d[h][1];
        if(check(x0, y0))
        {
            dfs(x0, y0);
        }
    }
}
int main()
{
    int i, j, t;
    while(scanf("%d %d", &m, &n) && m + n)
    {
        t = 0;      //一定要初始化
        memset(vis, 0, sizeof(vis));
        memset(a, 0, sizeof(a));
        for(i = 0; i < m; i++)
    {
        for(j = 0; j < n; j++)
        {
            cin>>a[i][j];
        }
    }
    for(i = 0; i < m; i++)
    {
        for(j = 0; j < n; j++)
            if(a[i][j] == '@' && !vis[i][j])
            {
                dfs(i, j);
                t++;
            }
    }
    printf("%d\n", t);
    }
    return 0;
}


1.输入字符型数组cin>>a[i][j];  用scanf会有点问题

2.这道题的建图:

起始点为第一层

起始点8个方向的是 @ 的点组成第二层

.........

直到找不到新的油田的位置为止

3.用一个数组来遍历方向

4.用一个check函数检查是否越界和重复

5.注意测试多组数据的时候一定要将计数器等常量初始化



BFS:从起始点,探索周围的点,直到结束
实现方式:队列
基本框架:

BFS(int x, inty, int z)


BFS(int x, inty, int z)
{
        queue<type> q;
         while(!q.empty())
           {
                   q.pop();
                  for(遍历四周)
                      if(满足条件)
                               q.push();
             }
}


example:

一. strange lift

There is a strange lift.The lift can stop can at every floor as you want, and there is a number Ki(0 <= Ki <= N) on every floor.The lift have just two buttons: up and down.When you at floor i,if you press the button "UP" , you will go up Ki floor,i.e,you will go to the i+Ki th floor,as the same, if you press the button "DOWN" , you will go down Ki floor,i.e,you will go to the i-Ki th floor. Of course, the lift can't go up high than N,and can't go down lower than 1. For example, there is a buliding with 5 floors, and k1 = 3, k2 = 3,k3 = 1,k4 = 2, k5 = 5.Begining from the 1 st floor,you can press the button "UP", and you'll go up to the 4 th floor,and if you press the button "DOWN", the lift can't do it, because it can't go down to the -2 th floor,as you know ,the -2 th floor isn't exist. 
Here comes the problem: when you are on floor A,and you want to go to floor B,how many times at least he has to press the button "UP" or "DOWN"? 
Input
The input consists of several test cases.,Each test case contains two lines. 
The first line contains three integers N ,A,B( 1 <= N,A,B <= 200) which describe above,The second line consist N integers k1,k2,....kn. 
A single 0 indicate the end of the input.
Output
For each case of the input output a interger, the least times you have to press the button when you on floor A,and you want to go to floor B.If you can't reach floor B,printf "-1".
Sample Input
5 1 5
3 3 1 2 5
0
Sample Output
3



代码如下:

#include <iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
int n, a, b, k[205];
bool vis[205];
struct lift
{
    int f;
    int s;
};

int bfs()
{
    queue<lift> q;
    q.push(lift{a, 0});  //结构体初始化的方式
    int r = -1;
    while(!q.empty())
    {
        lift m = q.front();
        vis[m.f] = 1;
        q.pop();
        if(m.f == b)
        {
            r = m.s;
            break;
        }
        int up = m.f + k[m.f - 1];
        int down = m.f - k[m.f - 1];
        if(up <= n && !vis[up])
            q.push(lift{up, m.s + 1});  //都是在这个m结构体下+1;
        if(down >= 1 && !vis[down])
            q.push(lift{down, m.s + 1});

    }

    while(!q.empty()) q.pop();  //清空队列
    return r;
}
int main()
{
    int i;
    while(scanf("%d", &n) && n != 0)
    {
        memset(vis, 0, sizeof(vis));
        scanf("%d %d", &a, &b);
        for(i  = 0; i < n; i++)
            scanf("%d", &k[i]);
        printf("%d\n", bfs());
    }
    return 0;
}

1.结构体的使用:

因为要记录下走的步数,所以不能单纯的整型,而是要使用结构体

结构体初始化的方式     q.push(lift{a(第一个量), 0(第二个量)});  

2.bfs函数

bfs函数主要就是队列,先让第一层进入队列,然后操作到第二层,再让所以第二层入队列

不断访问队列中的首元素,直到找到符合条件的元素。

3.清空队列 while(!q.empty()) q.pop();  


二.pet

One day, Lin Ji wake up in the morning and found that his pethamster escaped. He searched in the room but didn’t find the hamster. He tried to use some cheese to trap the hamster. He put the cheese trap in his room and waited for three days. Nothing but cockroaches was caught. He got the map of the school and foundthat there is no cyclic path and every location in the school can be reached from his room. The trap’s manual mention that the pet will always come back if it still in somewhere nearer than distance D. Your task is to help Lin Ji to find out how many possible locations the hamster may found given the map of the school. Assume that the hamster is still hiding in somewhere in the school and distance between each adjacent locations is always one distance unit.
Input
The input contains multiple test cases. Thefirst line is a positive integer T (0<T<=10), the number of test cases. For each test cases, the first line has two positive integer N (0<N<=100000) and D(0<D<N), separated by a single space. N is the number of locations in the school and D is the affective distance of the trap. The following N-1lines descripts the map, each has two integer x and y(0<=x,y<N), separated by a single space, meaning that x and y is adjacent in the map. Lin Ji’s room is always at location 0. 
Output
For each test case, outputin a single line the number of possible locations in the school the hamster may be found.
Sample Input
1
10 2
0 1
0 2
0 3
1 4
1 5
2 6
3 7
4 8
6 9
Sample Output
2


代码如下:

#include <iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
vector<int>e[100005];
int d;
struct pet
{
    int loc;
    int s;
};

int bfs()
{
    int t = 0;
    queue<pet>q;
    for(auto &v : e[0])	//现让起始位置的进队列
    {
        q.push({v, 0});
    }
    while(!q.empty())
    {
        pet m = q.front();
        q.pop();
        if(m.s >= d)    t++;
        for(auto &w : e[m.loc])
        {
            q.push({w, m.s + 1});
        }
    }
    return t;
}
int main()
{
    int i, n, N, a, b, j;
    scanf("%d", &N);
    for(i = 0; i < N; i ++)
    {
        for(j = 0; j < 100005; j++)
            e[j].clear();
        scanf("%d %d", &n, &d);
        for(j = 0; j < n - 1; j++)
        {
            scanf("%d %d", &a, &b);
            //if(a != 0) h = j;
            e[a].push_back(b);
        }
        printf("%d\n", bfs());
    }
    return 0;
}









<think>好的,用户想了解如何在C++中实现BFSDFS算法。首先,我需要回忆这两种算法的基本概念和它们的应用场景。根据提供的引用内容,BFS适合最短路径和层次遍历,而DFS适合深入探索和寻找全部解。接下来,我应该先解释两者的区别,然后给出具体的实现步骤和代码示例。 接下来,我需要确保代码的正确性。对于BFS,通常使用队列,从起始节点开始,逐层访问相邻节点。记得要标记已访问的节点,避免重复访问。DFS的话,可以用递归或者栈来实现,这里可能选择递归方式更直观,但要注意栈溢出的风险,不过对于教学示例,递归应该没问题。 然后,考虑到用户可能需要实际应用的例子,我应该提供一个图的表示方法,比如邻接表,并展示如何在这个图上运行BFSDFS。这样用户可以看到完整的流程,包括图的构建和算法的调用。 另外,用户可能会混淆这两种算法的应用场景,所以需要明确指出何时选择BFSDFS,比如引用中的内容提到BFS找最短路径,DFS适合全部解的情况。这些点需要强调,帮助用户做出正确的选择。 最后,生成相关问题时要围绕实现细节、性能比较和应用场景,确保问题能够帮助用户进一步理解和应用这两种算法。同时,检查引用标记是否正确,确保引用的内容与用户提供的资料一致。</think>### C++中BFSDFS的实现 #### 1. 广度优先搜索(BFS) **核心思想**:逐层遍历所有节点,利用队列实现。适用于最短路径、状态搜索等场景[^1][^2]。 **实现步骤**: 1. 创建队列和访问标记数组 2. 将起始节点入队并标记 3. 循环处理队列直到为空: - 取出队首节点 - 处理当前节点 - 将未访问的相邻节点入队并标记 **代码示例**: ```cpp #include <queue> #include <vector> void BFS(int start, const std::vector<std::vector<int>>& graph) { std::vector<bool> visited(graph.size(), false); std::queue<int> q; q.push(start); visited[start] = true; while (!q.empty()) { int current = q.front(); q.pop(); // 处理当前节点(例如打印) std::cout << current << " "; for (int neighbor : graph[current]) { if (!visited[neighbor]) { q.push(neighbor); visited[neighbor] = true; } } } } ``` #### 2. 深度优先搜索(DFS) **核心思想**:沿着分支深入探索到底,使用递归或栈实现。适合拓扑排序、路径探索等场景[^1][^2]。 **实现步骤**: 1. 创建访问标记数组 2. 递归访问节点: - 处理当前节点 - 标记为已访问 - 递归访问未访问的相邻节点 **代码示例**: ```cpp #include <vector> void DFS_Recursive(int node, const std::vector<std::vector<int>>& graph, std::vector<bool>& visited) { visited[node] = true; std::cout << node << " "; // 处理当前节点 for (int neighbor : graph[node]) { if (!visited[neighbor]) { DFS_Recursive(neighbor, graph, visited); } } } // 调用入口 void DFS(int start, const std::vector<std::vector<int>>& graph) { std::vector<bool> visited(graph.size(), false); DFS_Recursive(start, graph, visited); } ``` #### 3. 应用示例 **图的邻接表表示**: ```cpp /* 0 -> 1, 2 1 -> 3 2 -> 3 3 -> */ std::vector<std::vector<int>> graph = { {1, 2}, // 节点0的邻居 {3}, // 节点1的邻居 {3}, // 节点2的邻居 {} // 节点3的邻居 }; // 执行BFS BFS(0, graph); // 输出: 0 1 2 3 // 执行DFS DFS(0, graph); // 输出: 0 1 3 2 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值