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

GitHub跳转原题关系图返回列表

先把每个点的邻接表按升序排序,再用逆序压栈实现非递归 DFS,用队列实现 BFS。

OJ: luogu

题目 ID: P5318

难度:入门

标签:图论DFSBFS排序python

日期: 2026-06-19 19:24

题意

给出一个有向图,从 1 号点出发,分别输出:

  • 按题目要求进行的 DFS 遍历顺序
  • 按题目要求进行的 BFS 遍历顺序

如果某一步有多个可选点,必须先访问编号较小的那个。

思路

最直接的做法就是照定义写 DFS 和 BFS。

先看一个可以直接验证想法的朴素解:

cpp
#include <bits/stdc++.h>
using namespace std;

static vector<vector<int>> g;
static vector<int> vis;
static vector<int> dfs_order;
static vector<int> bfs_order;

void dfs(int u) {
    vis[u] = 1;
    dfs_order.push_back(u);
    for (int v : g[u]) {
        if (!vis[v]) {
            dfs(v);
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, m;
    cin >> n >> m;
    g.assign(n + 1, {});
    for (int i = 0; i < m; ++i) {
        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());
    }

    vis.assign(n + 1, 0);
    dfs(1);
    for (int x : dfs_order) {
        cout << x << ' ';
    }
    cout << '\n';

    fill(vis.begin(), vis.end(), 0);
    queue<int> q;
    q.push(1);
    vis[1] = 1;
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        bfs_order.push_back(u);
        for (int v : g[u]) {
            if (!vis[v]) {
                vis[v] = 1;
                q.push(v);
            }
        }
    }

    for (int x : bfs_order) {
        cout << x << ' ';
    }
    cout << '\n';
    return 0;
}

brute.cpp 用递归 DFS 和普通队列 BFS 实现了最直观的版本,适合小图对拍。

真正需要注意的是访问顺序和栈深:

  1. 邻接表必须先按升序排序
  2. BFS 直接按升序扩展邻居即可
  3. 正式解里的 DFS 不建议用递归,因为 n 可达 1e5

这张图展示样例结构:

digraph G {
  1 -> 2;
  1 -> 3;
  1 -> 4;
  2 -> 5;
  2 -> 6;
  3 -> 7;
  4 -> 7;
  4 -> 8;
  7 -> 8;
}

从图里可以看出,BFS 很自然是一层一层访问;而 DFS 则需要优先深入编号更小的后继。所以正式解采用“邻接表升序 + 逆序压栈”的方式,用显式栈模拟递归 DFS 的顺序。

Python 知识

  • dequepopleft()O(1)O(1),适合作为 BFS 队列。
  • 非递归 DFS 用列表作栈;邻接表升序后以 reversed(graph[node]) 逆序压栈,弹出时才会优先访问小编号。
  • bytearray(n+1) 比 Python 布尔列表更紧凑,适合十万点访问标记。
  • 逐行读入百万条边,避免 read().split() 同时保留两百万个临时 token。
  • /home/rainboy/mycode/hugo-blog/content/program_language/python/collections_toolkit.mddeque 队列和容器选择。
  • /home/rainboy/mycode/hugo-blog/content/program_language/python/cpp_to_python_pitfalls.md:递归深度与输入内存。

代码

python
import sys
from collections import deque


def main():
    read = sys.stdin.buffer.readline
    n, m = map(int, read().split())
    graph = [[] for _ in range(n + 1)]
    for _ in range(m):
        u, v = map(int, read().split())
        graph[u].append(v)
    for neighbors in graph:
        neighbors.sort()

    visited = bytearray(n + 1)
    dfs_order = []
    stack = [1]
    while stack:
        node = stack.pop()
        if visited[node]:
            continue
        visited[node] = 1
        dfs_order.append(node)
        stack.extend(reversed(graph[node]))

    visited = bytearray(n + 1)
    visited[1] = 1
    bfs_order = []
    queue = deque([1])
    while queue:
        node = queue.popleft()
        bfs_order.append(node)
        for neighbor in graph[node]:
            if not visited[neighbor]:
                visited[neighbor] = 1
                queue.append(neighbor)

    print(*dfs_order)
    print(*bfs_order)


if __name__ == "__main__":
    main()

复杂度

建图后需要对各邻接表排序,然后各做一次 DFS 和 BFS。时间复杂度上界为 O(mlogm+n+m)O(m\log m+n+m),空间复杂度 O(n+m)O(n+m)

总结

这题本身不难,关键在两个实现细节:邻接表要排序,非递归 DFS 要逆序压栈。把这两个点处理好,答案就稳定了。

一图流解析

这张图把本题的建模、关键转移、实现检查和训练方法压缩到一页,适合读完正文后复盘。

一图流解析