这道题是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);
}
};