图论基础-透析链式前向星

yxy 发布于 2025-04-02 最后更新于 2025-04-02 545 次阅读


声明

此文章仅为作者复习所用,无教学作用。

存图方法

链式前向星--一个O(n + m) 的存图策略,但是要注意读取的时候是反着来的。


1.查找文献 普及-

原题链接:https://www.luogu.com.cn/problem/P5318

题目描述

  • 小 K 喜欢翻看洛谷博客获取知识。每篇文章可能会有若干个(也有可能没有)参考文献的链接指向别的博客文章。小 K 求知欲旺盛,如果他看了某篇文章,那么他一定会去看这篇文章的参考文献(如果他之前已经看过这篇参考文献的话就不用再看它了)。
    假设洛谷博客里面一共有 n(n≤10^5) 篇文章(编号为 1 到 n)以及 m(m≤10^6) 条参考文献引用关系。目前小 K 已经打开了编号为 1 的一篇文章,请帮助小 K 设计一种方法,使小 K 可以不重复、不遗漏的看完所有他能看到的文章。这边是已经整理好的参考文献关系图,其中,文献 X → Y 表示文章 X 有参考文献 Y。不保证编号为 1 的文章没有被其他文章引用。                    请对这个图分别进行 DFS 和 BFS,并输出遍历结果。如果有很多篇文章可以参阅,请先看编号较小的那篇(因此你可能需要先排序)。
  • 输入格式:
    共 m+1 行,第 1 行为 2 个数,n 和 m,分别表示一共有 n(n≤10^5) 篇文章(编号为 1 到 n)以及m(m≤10^6) 条参考文献引用关系。
    接下来 m 行,每行有两个整数 X,Y 表示文章 X 有参考文献 Y。
  • 输出格式:共 2 行。 第一行为 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

思路

  • 题目要求先看编号小的那一篇,但是链式前向星是后序遍历来的,需要临时使用一个数组存下每一回合访问的编号,并进行排序(从小到大),然后依次存入答案数组中即可。时间复杂度O(m / logn)。

代码

链式前向星

const int N = 1e6 + 10;
int e[N] , ne[N] , h[N] , idx;
bool st[N];

void add(int a , int b)
{
    e[idx] = b , ne[idx] = h[a] , h[a] = idx ++;
}

void bfs(int s)
{
    vector<int> res;
    memset(st , false , sizeof st);
    queue<int> q;
    q.push(s);
    st[s] = true;
    res.push_back(s);
    
    while (q.size())
    {
        vector<int> temp;
        int index = q.front();
        q.pop();
        for (int i = h[index] ; ~i ; i = ne[i])
        {
            int j = e[i];   
            if (!st[j])
            {
                temp.push_back(j);
                st[j] = true;
            }   
        }

        sort(all(temp));

        for (auto i : temp)
        {
            q.push(i);
            res.push_back(i);
        }
    }

    for (auto i : res) cout << i << " ";

}

void dfs(int s)
{
    vector<int> temp;
    int index = s;
    
    st[index] = true;
    cout << index << " ";

    for (int i = h[index] ; ~i ; i = ne[i])
        temp.push_back(e[i]);

    sort(all(temp));

    for (auto i : temp) if (!st[i]) dfs(i);
}

void solve()
{
    memset(h , -1 , sizeof h);

    int n , m;
    cin >> n >> m;

    for (int i = 1 ; i <= m ; i ++ ) 
    {
        int a , b;
        cin >> a >> b;
        add(a , b);
    }

    dfs(1);
    cout << endl;
    bfs(1);
}

 

2.图的遍历 普及/提高−

原题链接:https://www.luogu.com.cn/problem/P3916

题目描述

  • 给出  个点, 条边的有向图,对于每个点 ,求  表示从点  出发,能到达的编号最大的点。
  • 输入格式:
    第  行  个整数 ,表示点数和边数。
    接下来  行,每行  个整数 ,表示边 。点用  编号。
  • 输出格式:一行  个整数 

示例

    • 输入:
      4 3
      1 2
      2 4
      4 3
  • 输出:
    4 4 3 4

思路

  • 最容易想到的是直接从每个点都出发做一次bfs,存下从当前点出发能到达的最大的点。通过率60%。时间复杂度:O(n²)。
  • 建立返图。直接从最大的那个点往钱遍历。第一次到的点一定是以当前点为最大的编号的点。如此可以节约很多时间。时间复杂度O(n + m).

代码

链式前向星 60%

const int N = 1e6 + 10;
int e[N] , ne[N] , h[N] , idx;
bool st[N];

void add(int a , int b)
{
    e[idx] = b , ne[idx] = h[a] , h[a] = idx ++;
}

void bfs(int s)
{
    int ans = 0;
    queue<int> q;
    q.push(s);
    memset(st , false , sizeof st);

    while (q.size())
    {
        int index = q.front();
        st[index] = true;
        q.pop();
        ans = max(ans , index);

        for (int i = h[index] ; ~i ; i = ne[i])
        {
            int j = e[i];
            if (!st[j])
            {
                st[j] = true;
                q.push(j);
            }
        }
    }
    cout << ans << " ";
}

void solve()
{
    memset(h , -1 , sizeof h);
    int n , m;
    cin >> n >> m;

    for (int i = 1 ; i <= m ; i ++ )
    {
        int a , b;
        cin >> a >> b;
        add(a , b);
    }

    for (int i = 1 ; i <= n ; i ++ ) bfs(i);
}

 

链式前向星 100%

const int N = 1e5 + 10 , M = 2 * N;
int e[M] , ne[M] , h[N] , idx;
int nums[N];

void add(int a , int b)
{
    e[idx] = b , ne[idx] = h[a] , h[a] = idx ++;
}

void bfs(int s)
{
    queue<int> q;
    q.push(s);

    while (q.size())
    {
        int index = q.front();
        q.pop();

        for (int i = h[index] ; ~i ; i = ne[i])
        {
            int j = e[i];
            if (!nums[j])
                q.push(j) , nums[j] = s;
        }
    }
}

void solve()
{
    memset(h , -1 , sizeof h);
    int n , m;
    cin >> n >> m;
    for (int i = 1 ; i <= m ; i ++ )
    {
        int a , b;
        cin >> a >> b;
        add(b , a);
    }

    for (int i = n ; i ; i -- )
    {
        if (!nums[i]) bfs(i) , nums[i] = i;
    }

    for (int i = 1 ; i <= n ; i ++ ) cout << nums[i] << " ";
}