图的存储与搜索

图的存储

存储一般使用邻接矩阵和邻接表两种方式。邻接矩阵中,g[i][j]表示节点i指向节点j的一条有向边。邻接矩阵需要O(n * n)的空间,因此我们常使用邻接表。

ps:一般稠密图用邻接矩阵,稀疏图用邻接表。稠密图中边的数量是节点个数的次方,也就是边数量远远大于节点数量。

邻接表是数组+链表的形式。比如节点1-6,那么h[1]就表示节点1直接连接的节点,h[1]指向一条链表。
image

代码中一般这样写:

#include <iostream>
#include <cstring>

using namespace std;

const int N = 1e5 + 10, M = N * 2;

int h[N], e[M], ne[M], idx;
bool st[N];

int n;

// 添加一条边,头插法加入链表
int add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

int main() {
    memset(h, -1, sizeof h);
    scanf("%d", &n);

    // 加入n条无向边,无向边可以看作两个节点互相指向的有向边
    for (int i = 0; i < n; i ++ ) {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b), add(b, a);
    }

    return 0;
}

图的搜索

dfs是深度优先搜索,借助语言本身的递归栈实现;bfs是广度优先搜索,常使用queue实现。dfs一般都用来求一些通用性质的值,比如求子树节点个数。bfs是层层向外寻找,具备最短路的性质,在边的权重是1时,可以用作寻找最短路。

由于使用邻接表存储图,在搜索时遍历到链表的某个节点a,就找到a所连接的链表,由此会有死循环问题,所以需要使用数组记录节点是否已经被使用过。

常用模版:

void dfs(int u) {
    st[u] = true;
    for (int i = h[u]; i != -1; i = ne[i]) {
        int v = e[i];
        if (!st[v]) {
           dfs(v);
        }
    }
}

void bfs() {
    // 初始化队列
    memset(d, -1, sizeof d);
    int hh = 0, tt = -1;
    q[ ++ tt] = 1;

    while (hh <= tt) {
        int t = q[hh ++ ];
        st[t] = true;

        for (int i = h[t]; i != -1; i = ne[i]) {
            int j = e[i];
            if (!st[j] == -1) {
                // 把满足要求的节点放入队列
                q[++ tt] = j;
            }
        }
    }
}

参照例题:
https://www.acwing.com/problem/content/848/
https://www.acwing.com/problem/content/849/

posted @ 2022-01-21 12:12  moon_orange  阅读(34)  评论(0编辑  收藏  举报