LeetCode 1971【并查集 DFS BFS】 寻找图中是否存在路径 HERODING的LeetCode之路

在这里插入图片描述
这道题是LeetCode中并查集标签下唯一一道简单题,是图论知识中最简单的问题,直接套用模板就可以得到结果,并查集、DFS、BFS都可以很好解决,首先给出DFS的解法。
DFS
第一步根据给定的边构建顶点邻接矩阵,定义访问数组,然后从source出发,不断往根据其相邻的边向下搜索,访问过的顶点标记为visited,直到找到destination返回true,否则返回false,代码如下:

class Solution {
public:
    bool validPath(int n, vector<vector<int>>& edges, int source, int destination) {
        // 构建邻接矩阵
        vector<vector<int>> adj(n);
        for(auto& edge : edges) {
            int x = edge[0], y = edge[1];
            adj[x].emplace_back(y);
            adj[y].emplace_back(x);
        }
        // 访问数组
        vector<bool> visited(n, false);
        return dfs(adj, source, destination, visited);
    }

    bool dfs(vector<vector<int>>& adj, int source, int destination, vector<bool>& visited) {
        if(source == destination) {
            return true;
        }
        visited[source] = true;
        for(auto next : adj[source]) {
            if(!visited[next] && dfs(adj, next, destination, visited)) {
                return true;
            }
        }
        return false;
    }
};

BFS
这道题同样可以用广搜轻松解决,广搜的方法和深搜有异曲同工之妙,首先都是构造邻接矩阵和访问数组,接着从source出发,把所有与source相连接的边放入队列中,并标记为visited,如果遇到destination返回true,不断重复把队列中的顶点相连接的未被访问过的顶点放入到队列中,直到队列为空,返回false,代码如下:

class Solution {
public:
    bool validPath(int n, vector<vector<int>>& edges, int source, int destination) {
        // 构建邻接矩阵
        vector<vector<int>> adj(n);
        for(auto& edge : edges) {
            int x = edge[0], y = edge[1];
            adj[x].emplace_back(y);
            adj[y].emplace_back(x);
        }
        // 访问数组
        vector<bool> visited(n, false);
        queue<int> q;
        q.push(source);
        visited[source] = true;
        while(!q.empty()) {
            int node = q.front();
            q.pop();
            if(node == destination) {
                return true;
            }
            for(auto& next : adj[node]) {
                if(!visited[next]) {
                    q.push(next);
                    visited[next] = true;
                }
            }
        }
        return false;
    }
};

并查集
事实上,解决连通图问题,最便捷直接的方法还是并查集,并查集相当于一个通用的模板,因此可以封装成类,当需要使用的时候直接copy过来即可,当然直接编写也很简单,首先是定义两个数据结构,parent和rank数组,前者是记录每个节点的父标签,即所在图标签,rank是判断两图合并时到底是谁吞并谁,在本题中不起作用(谁吞谁都一样),以及定义三个函数,第一个构造函数,初始化parent和rank,每个节点初始都是一张图,所以初始parent就是本身,find函数是为了找到当前节点所在图的根节点,即图标签,connect函数是判断两个节点是否有相同的根节点,即是否在同一张图上,第三个函数是union函数,将两个图连接到一起,如果两个节点的根节点相同,则不要合并,不同的话,就把rank大的吞并rank小的图(直接修改被吞并图的parent[root]即可)。构造好并查集类后,对于本题来说简直手到擒来,直接实例化并查集类,遍历edges把点连通,然后判断source和destination是否在同一个图中,即返回uf.connect(source, destination)即可,代码如下:

class UnionFind {
private:
    vector<int> parent;
    vector<int> rank;

public:
    UnionFind(int n) {
        parent = vector<int>(n);
        rank = vector<int>(n);
        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 connect(int x, int y) {
        return find(x) == find(y);
    }

    // 将两个连通图相连
    void unionGraph(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);
        if(rootX != rootY) {
            if(rank[rootX] > rank[rootY]) {
                parent[rootY] = rootX;
            } else if(rank[rootX] > rank[rootY]) {
                parent[rootX] = rootY; 
            } else {
                parent[rootY] = rootX;
                rank[rootX] ++;
            }
        }
    }
};

class Solution {
public:
    bool validPath(int n, vector<vector<int>>& edges, int source, int destination) {
        UnionFind uf(n);
        for(auto& edge : edges) {
            uf.unionGraph(edge[0], edge[1]);
        }
        return uf.connect(source, destination);
    }
};
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HERODING77

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值