关于并查集的一个小栗子
并查集主要应用于在算法题中涉及到图的部分,但是很多时候题目并不会直接了当的就把这道题能转化成一张图的这个意思给你透露出来。很多时候是自己去想一想,这题能不能采用并查集的思路来做。
下面是一个并查集的C++模板
class UF {
public:
vector<int> fa;
vector<int> sz;
int n;
int comp_cnt;
public:
UF(int _n): n(_n), comp_cnt(_n), fa(_n), sz(_n, 1) {
//iota函数的作用是把fa置为[0..n-1]
iota(fa.begin(), fa.end(), 0);
}
int findset(int x) {
return fa[x] == x ? x : fa[x] = findset(fa[x]);
}
void unite(int x, int y) {
x = findset(x);
y = findset(y);
if (x != y) {
if (sz[x] < sz[y]) {
swap(x, y);
}
fa[y] = x;
sz[x] += sz[y];
--comp_cnt;
}
}
bool connected(int x, int y) {
x = findset(x);
y = findset(y);
return x == y;
}
};
进阶一点的题目是最小体力消耗路径
核心的问题还是如何将题目转化成一张图的问题,转化成图的话,结点是什么,边是什么。
岛屿数量的结点就是哪些值为1的坐标值,如果该坐标的旁边存在值为1的坐标,则这两个结点存在一条边。
最长连续子序列的结点就是数组的值,如果存在连续值,则两者之间存在一条边。
最小体力消耗路径的巧妙之处是先对边进行排序,然后从小到大逐次加入到并查集当中,如果最后首位相连,则返回结果。
class UF {
public:
vector<int> fa;
vector<int> sz;
int n;
int comp_cnt;
public:
UF(int _n): n(_n), comp_cnt(_n), fa(_n), sz(_n, 1) {
iota(fa.begin(), fa.end(), 0);
}
int findset(int x) {
return fa[x] == x ? x : fa[x] = findset(fa[x]);
}
void unite(int x, int y) {
x = findset(x);
y = findset(y);
if (x != y) {
if (sz[x] < sz[y]) {
swap(x, y);
}
fa[y] = x;
sz[x] += sz[y];
--comp_cnt;
}
}
bool connected(int x, int y) {
x = findset(x);
y = findset(y);
return x == y;
}
};
class Solution {
public:
int minimumEffortPath(vector<vector<int>>& heights) {
vector<vector<int>> edges;
for(int i=0;i<heights.size();i++)
{
for(int j=0;j<heights[0].size();j++)
{
if(i+1<heights.size())
{
edges.push_back({abs(heights[i][j]-heights[i+1][j]),int(i*heights[0].size()+j),int((i+1)*heights[0].size()+j)});
}
if(j+1<heights[0].size())
{
edges.push_back({abs(heights[i][j]-heights[i][j+1]),int(i*heights[0].size()+j),int(i*heights[0].size()+j+1)});
}
}
}
sort(edges.begin(),edges.end());
UF uf(heights.size() * heights[0].size());
for(auto c:edges)
{
uf.unite(c[1],c[2]);
if(uf.connected(0,heights.size()*heights[0].size()-1))
{
return c[0];
}
}
return 0;
}
};