【算法 02】一题学习BFS和DFS算法

一题学习BFS和DFS算法


请添加图片描述

洛谷题目解析:【深基18.例3】查找文献

题目背景

小K热衷于在洛谷博客上阅读文章并探索其中的知识。每篇文章都可能包含指向其他博客文章的参考文献链接。小K的求知欲非常强,如果他阅读了某篇文章,他一定会去查看这篇文章的所有参考文献(但前提是这些参考文献他之前还没看过)。现在,我们帮助小K设计一种方法,确保他能不重复、不遗漏地阅读完所有他能看到的文章。

题目描述

洛谷博客共有n(n≤105)篇文章(编号为1到n)和m(m≤106)条参考文献引用关系。小K已经打开了编号为1的文章。我们需要通过深度优先搜索(DFS)和广度优先搜索(BFS)两种方式,来模拟小K阅读文章的过程,并输出遍历的结果。

输入输出格式

输入格式

  • 第一行包含两个整数nm,分别表示文章总数和参考文献关系数。
  • 接下来m行,每行包含两个整数X,Y,表示文章X有参考文献Y

输出格式

  • 第一行输出DFS遍历的结果。
  • 第二行输出BFS遍历的结果。

样例

样例输入

8 9  
1 2  
1 3  
1 4  
2 5  
2 6  
3 7  
4 7  
4 8  
7 8

样例输出

1 2 5 6 3 7 8 4  
1 2 3 4 5 6 7 8

解题思路

深度优先搜索(DFS)

DFS是一种用于遍历或搜索树或图的算法。它从根节点开始,尽可能深地搜索树的分支,直到达到叶子节点,然后回溯到前一个节点,继续探索尚未探索的分支。在DFS中,我们使用递归来实现这一过程。

广度优先搜索(BFS)

BFS也是一种遍历或搜索图或树的算法。它从根节点开始,首先访问根节点的所有相邻节点,然后对每个相邻节点,再访问它们的所有未被访问的相邻节点,以此类推,直到所有节点都被访问。在BFS中,我们通常使用队列来实现这一过程。

代码实现

以下是C++的实现代码,包含了DFS和BFS的具体实现:

#include<iostream>  
#include<vector>  
#include<queue>  
#include<algorithm>  
using namespace std;  
  
const int MAXN = 100010;  
vector<int> graph[MAXN];  
bool visitedDFS[MAXN], visitedBFS[MAXN];  
  
// DFS实现  
void dfs(int node) {  
    if (visitedDFS[node]) return;  
    visitedDFS[node] = true;  
    cout << node << " ";  
    for (int neighbor : graph[node]) {  
        dfs(neighbor);  
    }  
}  
  
// BFS实现  
void bfs(int start) {  
    queue<int> q;  
    q.push(start);  
    visitedBFS[start] = true;  
    while (!q.empty()) {  
        int current = q.front();  
        q.pop();  
        cout << current << " ";  
        for (int neighbor : graph[current]) {  
            if (!visitedBFS[neighbor]) {  
                q.push(neighbor);  
                visitedBFS[neighbor] = true;  
            }  
        }  
    }  
}  
  
int main() {  
    int n, m;  
    cin >> n >> m;  
    for (int i = 0; i < m; i++) {  
        int from, to;  
        cin >> from >> to;  
        graph[from].push_back(to);  
    }  
    // 由于题目要求先访问编号较小的文章,我们不需要对邻接表排序  
    dfs(1);  
    cout << endl;  
    fill(visitedBFS, visitedBFS + MAXN, false); // 清除BFS的访问标记  
    bfs(1);  
    return 0;  
}

当然,以下是这段代码中算法部分的详细讲解:

1. 图的表示

代码使用了一个邻接表(vector<int> g[MAX];)来表示图。g[i] 是一个存储了所有与节点 i 相邻的节点编号的 vector。这种表示法适用于稀疏图,因为它只存储实际存在的边,而不是为每对可能的节点都保留一个空间(如邻接矩阵)。

2. 深度优先搜索(DFS)

dfs 函数实现了深度优先搜索算法。它从给定的起始节点 u 开始,递归地探索图:

  • 首先检查节点 u 是否已经被访问过(通过 used[u] 数组)。如果是,则直接返回,避免重复访问。
  • 将节点 u 标记为已访问(used[u] = 1;)。
  • 输出节点 u 的编号。
  • 然后,对于节点 u 的每一个邻接节点 e,递归调用 dfs(e)

3. 广度优先搜索(BFS)

bfs 函数实现了广度优先搜索算法。它从给定的起始节点 u 开始,逐层遍历图:

  • 使用一个队列 q 来存储待访问的节点。首先将起始节点 u 加入队列。
  • 当队列不为空时,循环执行以下操作:
    • 从队列中取出一个节点 uq.front()),然后将其从队列中移除(q.pop())。
    • 检查节点 u 是否已经被访问过(通过 used2[u] 数组)。如果是,则跳过当前循环的剩余部分,继续处理下一个节点。
    • 输出节点 u 的编号。
    • 将节点 u 标记为已访问(used2[u] = 1;)。
    • 对于节点 u 的每一个邻接节点 e,将其加入队列 q 中以便后续访问。

4. 输入与输出

  • 输入部分首先读取两个整数 ab,其中 a 表示图中节点的数量(尽管 g 数组的大小已经通过 MAX 定义为 100010,但 a 可能用于后续验证或处理),b 表示边的数量。
  • 然后,读入 b 条边,每条边由两个整数 cd 表示,意味着存在一条从节点 c 到节点 d 的边。这些边被添加到邻接表中。
  • 在遍历所有边之后,对每个节点的邻接节点进行排序(这可能是为了某种特定的输出顺序或后续处理的需要)。
  • 最后,从节点 1 开始分别执行 DFS 和 BFS,并输出访问节点的顺序。DFS 的输出将展示从节点 1 开始的一条深度优先遍历路径,而 BFS 的输出将展示从节点 1 开始逐层向外扩展的广度优先遍历路径。

5. 注意事项

  • usedused2 数组分别用于 DFS 和 BFS 的访问标记,以避免重复访问节点。

  • 在实际应用中,如果图是有向的,则这种表示和遍历方法是有效的。如果图是无向的,那么可能需要在添加边时同时添加两个方向的边(即 g[c].push_back(d);g[d].push_back(c);)。

  • 排序邻接节点可能是为了特定的输出要求或算法优化,但在某些情况下可能不是必需的。

  • 题目中提到“不保证编号为1的文章没有被其他文章引用”,但在这个问题中,我们是从编号为1的文章开始遍历,所以这一点不影响我们的遍历过程。

  • 在DFS中,我们使用了递归函数来深入探索每一个分支。当遇到已经访问过的节点时,我们会直接返回,避免重复遍历。

  • 在BFS中,我们使用了队列来存储待访问的节点。每次从队列中取出一个节点进行访问,并将其所有未访问的邻接节点加入队列中。这样可以保证我们总是先访问离起始节点最近的节点。

  • 注意,在每次使用完DFS或BFS后,我们都需要重置访问标记数组,以便下一次遍历能够正确进行。但在本题中,由于我们只进行了一次DFS和一次BFS,所以只需在BFS前重置BFS的访问标记数组即可。

  • 最后,由于题目要求输出编号较小的文章在前,而我们在读入边时,默认将每个节点的邻接节点按照输入顺序存储在了邻接表中。由于C++的vector在遍历时会按照元素被添加的顺序进行,所以自然满足了这个要求,我们无需对邻接表进行额外的排序操作。

    总结

    通过这道题,我们复习了图的深度优先搜索(DFS)和广度优先搜索(BFS)两种遍历算法,并了解了它们在实际问题中的应用。同时,我们也学会了如何使用邻接表来表示图,并如何通过递归和队列来实现DFS和BFS的遍历过程。希望这次解析能够帮助大家更好地理解和掌握这两种重要的图遍历算法。

  • 46
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BFSDFS都是常用的搜索算法。它们的区别在于搜索的策略和复杂度。引用中提到,对于给定的问题,BFS是较佳的算法BFS(广度优先搜索)是一种逐层扩展搜索的算法。它从起始节点开始,逐层遍历邻接节点,直到找到目标节点或遍历完整个BFS使用队列来存储待访问的节点,确保按照层级的顺序进行搜索。BFS算法的时间复杂度为O(V + E),其中V是节点的数量,E是边的数量。 DFS深度优先搜索)是一种递归实现的搜索算法。它从起始节点开始,不断沿着路径深入直到无法继续或找到目标节点,然后回溯到上一个节点,继续探索其他路径。DFS使用栈来存储待访问的节点,它的搜索路径是深度优先的。DFS算法的时间复杂度为O(V + E),其中V是节点的数量,E是边的数量。 在实际应用中,BFSDFS都有各自的优缺点。BFS通常用于解决最短路径和最小生成树等问题,而DFS更适合用于寻找所有可能的解,如的连通性和拓扑排序等问题。选择使用哪种算法取决于具体的问题和需求。引用中提到,我们在学习数据结构时通常会接触到BFSDFS算法,尤其是在的遍历和二叉树的遍历中经常用到。 总结起来,BFSDFS是常用的搜索算法,它们在搜索策略和复杂度上有不同。BFS逐层扩展搜索,适用于最短路径和最小生成树等问题。DFS深度优先搜索,适用于寻找所有可能解的问题。具体选择哪种算法取决于问题的特点和需求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [熬夜怒肝,算法BFSDFS的直观解释](https://blog.csdn.net/c406495762/article/details/117307841)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值