【图的连通性】【并查集】【拓扑排序】

在图论中,不同类型的图(无向图和有向图)需要使用不同的算法和数据结构来处理它们的特性和问题。这里我们将讨论如何使用并查集来解决无向图的连通性问题,以及如何使用深度优先搜索(DFS)、广度优先搜索(BFS)和拓扑排序来解决有向图中的依赖性问题。

无向图的连通性:并查集

对于无向图的连通性问题,并查集(Union-Find)是一个非常有效的数据结构。它可以用于快速合并集合和查找元素所属的集合,从而帮助我们判断两个节点是否在同一个连通分量中。

示例:判断无向图的连通性

#include <vector>
#include <iostream>

class UnionFind {
public:
    UnionFind(int n) {
        parent.resize(n);
        rank.resize(n, 0);
        for (int i = 0; i < n; ++i) {
            parent[i] = i;
        }
    }
    
    int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]); // 路径压缩
        }
        return parent[x];
    }
    
    bool unionSet(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);
        if (rootX == rootY) {
            return false; // x 和 y 已经在同一个集合中
        }
        if (rank[rootX] > rank[rootY]) {
            parent[rootY] = rootX;
        } else if (rank[rootX] < rank[rootY]) {
            parent[rootX] = rootY;
        } else {
            parent[rootY] = rootX;
            rank[rootX]++;
        }
        return true;
    }

private:
    std::vector<int> parent;
    std::vector<int> rank;
};

int main() {
    int n = 5; // 节点数
    UnionFind uf(n);
    
    // 添加边
    std::vector<std::pair<int, int>> edges = {{0, 1}, {1, 2}, {3, 4}};
    for (const auto& edge : edges) {
        uf.unionSet(edge.first, edge.second);
    }
    
    // 检查连通性
    std::cout << "0 and 2 are connected: " << (uf.find(0) == uf.find(2)) << std::endl; // 输出 1 (true)
    std::cout << "0 and 4 are connected: " << (uf.find(0) == uf.find(4)) << std::endl; // 输出 0 (false)
    
    return 0;
}

有向图的依赖性:DFS、BFS、拓扑排序

对于有向图的依赖性问题,例如检测是否存在环、确定任务的执行顺序等,我们通常使用深度优先搜索(DFS)、广度优先搜索(BFS)以及拓扑排序。

拓扑排序

拓扑排序是一种对有向无环图(DAG)进行排序的方法,使得对于每条边 ( u \to v ),顶点 ( u ) 在排序后序列中出现在顶点 ( v ) 之前。

使用 DFS 进行拓扑排序
#include <vector>
#include <stack>
#include <iostream>

class Graph {
public:
    Graph(int vertices) : adj(vertices), V(vertices) {}
    
    void addEdge(int u, int v) {
        adj[u].push_back(v);
    }
    
    void topologicalSort() {
        std::vector<bool> visited(V, false);
        std::stack<int> Stack;
        
        for (int i = 0; i < V; i++) {
            if (!visited[i]) {
                topologicalSortUtil(i, visited, Stack);
            }
        }
        
        // 打印拓扑排序
        while (!Stack.empty()) {
            std::cout << Stack.top() << " ";
            Stack.pop();
        }
        std::cout << std::endl;
    }

private:
    int V;
    std::vector<std::vector<int>> adj;
    
    void topologicalSortUtil(int v, std::vector<bool>& visited, std::stack<int>& Stack) {
        visited[v] = true;
        
        for (int i : adj[v]) {
            if (!visited[i]) {
                topologicalSortUtil(i, visited, Stack);
            }
        }
        
        Stack.push(v);
    }
};

int main() {
    Graph g(6);
    g.addEdge(5, 2);
    g.addEdge(5, 0);
    g.addEdge(4, 0);
    g.addEdge(4, 1);
    g.addEdge(2, 3);
    g.addEdge(3, 1);
    
    std::cout << "拓扑排序结果: ";
    g.topologicalSort();
    
    return 0;
}
使用 Kahn 算法(BFS)进行拓扑排序
#include <vector>
#include <queue>
#include <iostream>

class Graph {
public:
    Graph(int vertices) : adj(vertices), V(vertices) {}

    void addEdge(int u, int v) {
        adj[u].push_back(v);
    }

    void topologicalSort() {
        std::vector<int> in_degree(V, 0);

        for (int u = 0; u < V; u++) {
            for (int v : adj[u]) {
                in_degree[v]++;
            }
        }

        std::queue<int> q;
        for (int i = 0; i < V; i++) {
            if (in_degree[i] == 0) {
                q.push(i);
            }
        }

        int count = 0;
        std::vector<int> top_order;

        while (!q.empty()) {
            int u = q.front();
            q.pop();
            top_order.push_back(u);

            for (int v : adj[u]) {
                if (--in_degree[v] == 0) {
                    q.push(v);
                }
            }

            count++;
        }

        if (count != V) {
            std::cerr << "存在环,无法进行拓扑排序。" << std::endl;
            return;
        }

        for (int i : top_order) {
            std::cout << i << " ";
        }
        std::cout << std::endl;
    }

private:
    int V;
    std::vector<std::vector<int>> adj;
};

int main() {
    Graph g(6);
    g.addEdge(5, 2);
    g.addEdge(5, 0);
    g.addEdge(4, 0);
    g.addEdge(4, 1);
    g.addEdge(2, 3);
    g.addEdge(3, 1);
    
    std::cout << "拓扑排序结果: ";
    g.topologicalSort();
    
    return 0;
}

解释

  1. DFS 拓扑排序

    • 通过递归访问每个节点,并在所有相邻节点都访问完后,将当前节点压入栈中。
    • 最后从栈中弹出节点就是拓扑排序的顺序。
  2. Kahn 算法(BFS 拓扑排序)

    • 计算每个节点的入度。
    • 将所有入度为0的节点入队。
    • 不断从队列中取出入度为0的节点,加入拓扑排序序列,并减少其相邻节点的入度。
    • 如果在排序结束时仍存在入度不为0的节点,则说明图中存在环。

适用场景

  • 无向图的连通性问题:使用并查集。
  • 有向图的依赖性问题:使用DFS或BFS进行拓扑排序。
posted @ 2024-05-14 16:00  peterzh6  阅读(149)  评论(0编辑  收藏  举报