树与图

86 阅读1分钟

本文已参与[新人创作礼]活动,一起开启掘金创作者之路。

树与图的存储 树是一种特殊的图,与图的存储方式相同。 对于无向图中的边ab,存储两条有向边a->b, b->a。 因此我们可以只考虑有向图的存储。

(1) 邻接矩阵:g[a][b] 存储边a->b

(2) 邻接表:

// 对于每个点k,开一个单链表,存储k所有可以走到的点。h[k]存储这个单链表的头结点 int h[N], e[N], ne[N], idx;

// 添加一条边a->b void add(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ; }

// 初始化 idx = 0; memset(h, -1, sizeof h); 树与图的遍历 时间复杂度 O(n+m)O(n+m), nn 表示点数,mm 表示边数 (1) 深度优先遍历 —— 模板题 AcWing 846. 树的重心

int dfs(int u) { st[u] = true; // st[u] 表示点u已经被遍历过

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

} (2) 宽度优先遍历 —— 模板题 AcWing 847. 图中点的层次

queue q; st[1] = true; // 表示1号点已经被遍历过 q.push(1);

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

for (int i = h[t]; i != -1; i = ne[i])
{
    int j = e[i];
    if (!st[j])
    {
        st[j] = true; // 表示点j已经被遍历过
        q.push(j);
    }
}

} 拓扑排序 时间复杂度 O(n+m)O(n+m), nn 表示点数,mm 表示边数 bool topsort() { int hh = 0, tt = -1;

// d[i] 存储点i的入度
for (int i = 1; i <= n; i ++ )
    if (!d[i])
        q[ ++ tt] = i;

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

    for (int i = h[t]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (-- d[j] == 0)
            q[ ++ tt] = j;
    }
}

// 如果所有点都入队了,说明存在拓扑序列;否则不存在拓扑序列。
return tt == n - 1;

} 朴素dijkstra算法 时间复杂是 O(n2+m)O(n2+m), nn 表示点数,mm 表示边数 int g[N][N]; // 存储每条边 int dist[N]; // 存储1号点到每个点的最短距离 bool st[N]; // 存储每个点的最短路是否已经确定

// 求1号点到n号点的最短路,如果不存在则返回-1 int dijkstra() { memset(dist, 0x3f, sizeof dist); dist[1] = 0;

for (int i = 0; i < n - 1; i ++ )
{
    int t = -1;     // 在还未确定最短路的点中,寻找距离最小的点
    for (int j = 1; j <= n; j ++ )
        if (!st[j] && (t == -1 || dist[t] > dist[j]))
            t = j;

    // 用t更新其他点的距离
    for (int j = 1; j <= n; j ++ )
        dist[j] = min(dist[j], dist[t] + g[t][j]);

    st[t] = true;
}

if (dist[n] == 0x3f3f3f3f) return -1;
return dist[n];

}

y总算法学习