5318 【深基18.例3】查找文献

题目理解

这道题目要求我们对一个有向图进行DFS(深度优先搜索)和BFS(广度优先搜索)遍历,并按照特定规则输出遍历顺序。题目中的关键点包括:

  1. 图的构建:文章间的引用关系构成一个有向图

  2. 遍历规则:从文章1开始,当有多个可选文章时,优先选择编号较小的

  3. 输出要求:分别输出DFS和BFS的遍历顺序

解题思路

1. 图的表示

使用邻接表(vector<int> g[N])来存储图的边关系,因为:

  • 文章编号范围是1到n(n ≤ 10^5)

  • 边数m ≤ 10^6

  • 邻接表适合稀疏图且空间效率高

2. 处理输入

读取n和m后,读取m条边关系,构建邻接表:

while(m--) {
    int u, v;
    cin >> u >> v;
    g[u].push_back(v); // 添加u→v的边
}

3. 邻接表排序

题目要求"先看编号较小的那篇",因此需要对每个节点的邻接表进行排序:

for(int i = 1; i <= n; i++) {
    sort(g[i].begin(), g[i].end());
}

这一步确保在DFS和BFS时,总是优先访问编号较小的节点。

4. DFS实现

深度优先搜索采用递归实现:

void dfs(int x) {
    vis[x] = 1;       // 标记已访问
    cout << x << " "; // 输出当前节点
    for(int v : g[x]) { // 遍历邻接节点
        if(vis[v] == 0) { // 如果未访问
            dfs(v);       // 递归访问
        }
    }
}

特点:尽可能深地探索图的分支,适合发现图的深层结构。

5. BFS实现

广度优先搜索使用队列实现:

void bfs(int x) {
    queue<int> q;
    q.push(x);        // 初始节点入队
    vis[x] = 1;       // 标记已访问
    cout << x << " "; // 输出当前节点
    while(!q.empty()) {
        int u = q.front(); // 取出队首
        q.pop();
        for(int v : g[u]) { // 遍历邻接节点
            if(vis[v] == 0) { // 如果未访问
                cout << v << " "; // 输出节点
                vis[v] = 1;     // 标记已访问
                q.push(v);      // 入队等待处理
            }
        }
    }
}

特点:按层次遍历图,适合寻找最短路径。

6. 主函数流程

int main() {
    // 输入处理
    // 邻接表排序
    
    dfs(1); // 从文章1开始DFS
    cout << endl; // 换行分隔
    
    memset(vis, 0, sizeof(vis)); // 重置访问标记
    
    bfs(1); // 从文章1开始BFS
    
    return 0;
}

代码注释详解

#include<bits/stdc++.h>
#define N 100005 // 定义最大节点数
#define endl "\n"
using namespace std;

int n, m; // 文章数和引用关系数
vector<int> g[N]; // 邻接表存储图
bool vis[N]; // 访问标记数组

// 深度优先搜索
void dfs(int x) {
    vis[x] = 1; // 标记当前节点已访问
    cout << x << " "; // 输出当前节点
    for(int v : g[x]) { // 遍历所有邻接节点
        if(vis[v] == 0) { // 如果邻接节点未被访问
            dfs(v); // 递归访问
        }
    }
}

// 广度优先搜索
void bfs(int x) {
    queue<int> q; // 创建队列
    q.push(x); // 起始节点入队
    vis[x] = 1; // 标记已访问
    cout << x << " "; // 输出起始节点
    while(!q.empty()) { // 队列不为空时循环
        int u = q.front(); // 获取队首元素
        q.pop(); // 队首出队
        for(int v : g[u]) { // 遍历当前节点的邻接节点
            if(vis[v] == 0) { // 如果邻接节点未被访问
                cout << v << " "; // 输出节点
                vis[v] = 1; // 标记已访问
                q.push(v); // 邻接节点入队
            }
        }
    }
}

int main() {
    cin >> n >> m; // 输入文章数和引用关系数
    while(m--) { // 处理每条引用关系
        int u, v;
        cin >> u >> v;
        g[u].push_back(v); // 构建邻接表
    }
    
    // 对每个节点的邻接表排序,确保优先访问编号小的文章
    for(int i = 1; i <= n; i++) {
        sort(g[i].begin(), g[i].end());
    }

    dfs(1); // 执行DFS
    cout << endl; // 换行
    
    memset(vis, 0, sizeof(vis)); // 重置访问标记数组
    
    bfs(1); // 执行BFS

    return 0;
}

复杂度分析

  1. 时间复杂度

    • 邻接表排序:O(n + m log d),其中d是平均出度

    • DFS/BFS:O(n + m)

    • 总体:O(n + m log d)

  2. 空间复杂度

    • 邻接表存储:O(n + m)

    • 访问数组:O(n)

    • 队列/递归栈:最坏O(n)

posted @ 2025-05-22 20:36  行胜于言Ibl  阅读(14)  评论(0)    收藏  举报