并查集(Union-find)及经典问题

并查集的基础知识

Quick-Find算法

染色方法:

  1. 基于染色的思想,一开始所有点的颜色不同
  2. 连接两个点的操作,可以看成将一种颜色的点染成另一种颜色
  3. 如果两个点颜色一样,证明联通,否则不联通
  4. 这中方法叫做并查集的:【Quick-Find 算法】
    联通判断O(1) 合并操作O(n)
class UnionSet {
public: 
	int *color, n;
	UnionSet(int n): n(n){
		color = new int[n + 1];
		for(int i = 0; i <= n; i++){
			color[i] = i;
		}
	}
	int find(int x){
		return color[x];
	}
	void merge(int a, int b){
		if(color[a] === color[b]) return;
		int cb = color[b];
		for(int i = 0;i <= n; i++){
			if(color[i] === cb) color[i] = color[a];
		}
		return;
	}
}
int main(){
	
	return 0;
}
  1. quick-find算法的联通判断非常快,可是合并操作非常慢
  2. 本质上问题中只是需要知道一个点与哪些点的颜色相同
  3. 而若干点的颜色可以通过间接指向同一个节点
  4. 合并操作时,实际上是将一棵树作为另一棵树的子树

Quick-Union算法

使用树型结构
联通判断 与tree-height 树高有关 合并操作O(n) 与tree-height 树高有关

class UnionSet {
public:
	int *boss, n;
	UnionSet(int n): n(n){
		boss = new int[n + 1];
		for(int i = 0; i <= n ; i++){
			boss[i] = i;
		}
	}
	int find(int x){
		if(boss[x] === x) return x;
		return find(boss[x]);
	}
	void merge(int a, int b){
		int fa = find(a), fb = find(b);
		if(fa === fb) return;
		boss[fa] = fb;
		return;
	}
}
int main(){
	return;
}

有效的减少树高能够提高效率

  1. 极端情况下会退化成一条链
  2. 将节点数量多的接到少的树上面,导致了退化
  3. 将树高深的接到浅的上面,导致了退化

问题:若要改进,是按照节点数量还是按照树的高度为合并参考?
指标:平均查找次数 = 所有节点的深度数量相加/ 总节点数量
节点少的作为儿子,节点多的作为爸爸

优化后:

class UnionSet {
public:
	int *fa, *size, n;
	UnionSet(int n): n(n){
		fa = new int[n + 1];
		size = new int[n + 1];
		for(int i = 0; i <= n; i++){
			fa[i] = i;
			size[i] = 1;
		}
	}
	int find(int x){
		if(fa[x] == x) return x;
		return find(fa[x]);
	}
	void merge(int a, int b){
		int ra = find(a), rb = find(b);
		if(ra == rb) return;
		if(size[ra] < size[rb]){
			fa[ra] = rb;
			size[rb] += size[ra];
		}else{
			fa[rb] = ra;
			size[ra] += size[rb];
		}
		return;
	}
}
int main(){
	return;
}

对树进行扁平化操作,让效率变高

class UnionSet {
public:
	int *fa, *size, n;
	UnionSet(int n): n(n){
		fa = new int[n + 1];
		size = new int[n + 1];
		for(int i = 0; i <= n; i++){
			fa[i] = i;
			size[i] = 1;
		}
	}
	int find(int x){
		if(fa[x] == x) return x;
		int root = find(fa[x]);
		fa[x] = root;
		return root;
	}
	void merge(int a, int b){
		int ra = find(a), rb = find(b);
		if(ra == rb) return;
		if(size[ra] < size[rb]){
			fa[ra] = rb;
			size[rb] += size[ra];
		}else{
			fa[rb] = ra;
			size[ra] += size[rb];
		}
		return;
	}
}
int main(){
	return;
}

并查集模版代码

class UnionSet {
public:
	int *fa, n;
	UnionSet(int n): n(n){
		fa = new int[n + 1];
		for(int i = 0;i <= n; i++){
			fa[i] = i;
		}
	}
	int get(int x){
		return fa[x] = (fa[x] == x ? x : get(fa[x]));
	}
	void merge(int a, int b){
		fa[get(a)] = get(b);
	}
};

int main(){
	return 0;
}

百变大咖:并查集
并查集是一类抽象化程度很高的数据结构

经典面试题 - 并查集基础题目

547. 省份数量


class Solution {
public:
    class UnionSet {
    public:
        int *fa, n;
        UnionSet(int n): n(n){
            fa = new int[n + 1];
            for(int i = 0;i <= n; i++){
                fa[i] = i;
            }
        }
        int get(int x){
            return fa[x] = (fa[x] == x ? x : get(fa[x]));
        }
        void merge(int a, int b){
            fa[get(a)] = get(b);
        }
    };

    int findCircleNum(vector<vector<int>>& isConnected) {
        int n = isConnected.size();
        UnionSet u(n);
        for(int i = 0; i < n; i++){
            for(int j = 0; j < i; j++){
                if(isConnected[i][j]) u.merge(i, j);
            }
        }
        int ans = 0;
        for(int i = 0; i < n; i++){
            if(u.get(i) == i) ans += 1;
        }
        return ans;
    }
};

200.岛屿数量

  • 如果当前点和上面的点都是1,就把当前点和上面的点进行联通
  • 如果当前点和左边的点都是1,就把当前点和左边的点进行联通
  • 最后统计并查集中根节点的数量
class Solution {
public:
    class UnionSet {
    public:
        int *fa, n;
        UnionSet(int n): n(n){
            fa = new int[n + 1];
            for(int i = 0;i <= n; i++){
                fa[i] = i;
            }
        }
        int get(int x){
            return fa[x] = (fa[x] == x ? x : get(fa[x]));
        }
        void merge(int a, int b){
            fa[get(a)] = get(b);
        }
    };

    int numIslands(vector<vector<char>>& grid) {
        int n = grid.size(), m = grid[0].size();
        UnionSet u(n * m);
        #define ind(i, j) ((i) * m + (j))
        for(int i = 0 ; i < n; i++){
            for(int j = 0; j < m; j++){
                if(grid[i][j] == '0') continue;
                if(i > 0 && grid[i - 1][j] == '1') u.merge(ind(i, j), ind(i - 1, j));
                if(j > 0 && grid[i][j - 1] == '1') u.merge(ind(i, j), ind(i, j - 1));
             
            }
        }
        int ans = 0;
        for(int i = 0; i < n; i++){
            for(int j = 0;j < m; j++){
                if(grid[i][j] == '1' && u.get(ind(i, j)) == ind(i, j)) ans += 1;
            }
        }
        #undef ind;
        return ans;
    }
};

990. 等式方程的可满足性

class Solution {
public:
    class UnionSet {
    public:
        int *fa, n;
        UnionSet(int n): n(n){
            fa = new int[n + 1];
            for(int i = 0;i <= n; i++){
                fa[i] = i;
            }
        }
        int get(int x){
            return fa[x] = (fa[x] == x ? x : get(fa[x]));
        }
        void merge(int a, int b){
            fa[get(a)] = get(b);
        }
    };
    bool equationsPossible(vector<string>& equations) {
        UnionSet u(26);
        for(auto s: equations){
            if(s[1] == '!') continue;
            int a = s[0] - 'a';
            int b = s[3] - 'a';
            u.merge(a, b);
        }
        for(auto s: equations){
            if(s[1] == '=') continue;
            int a = s[0] - 'a';
            int b = s[3] - 'a';
            if (u.get(a) == u.get(b)) return false;
        }
        return true;
    }
};

684. 冗余连接

class Solution {
public:
    class UnionSet {
    public:
        int *fa, n;
        UnionSet(int n): n(n){
            fa = new int[n + 1];
            for(int i = 0;i <= n; i++){
                fa[i] = i;
            }
        }
        int get(int x){
            return fa[x] = (fa[x] == x ? x : get(fa[x]));
        }
        void merge(int a, int b){
            fa[get(a)] = get(b);
        }
    };

    vector<int> findRedundantConnection(vector<vector<int>>& edges) {
        UnionSet u(edges.size());
        for(auto e: edges){
            int a = e[0];
            int b = e[1];
            if(u.get(a) == u.get(b)) return e;
            u.merge(a, b);
        }
        return vector<int>();
    }
};

1319. 连通网络的操作次数

class Solution {
public:
    class UnionSet {
    public:
        int *fa, n;
        UnionSet(int n): n(n){
            fa = new int[n + 1];
            for(int i = 0;i <= n; i++){
                fa[i] = i;
            }
        }
        int get(int x){
            return fa[x] = (fa[x] == x ? x : get(fa[x]));
        }
        void merge(int a, int b){
            fa[get(a)] = get(b);
        }
    };
    int makeConnected(int n, vector<vector<int>>& connections) {
        if(connections.size() < n - 1) return -1;
        UnionSet u(n);
        for(auto e: connections){
            int a = e[0];
            int b = e[1];
            u.merge(a, b);
        }
        int cnt = 0;
        for(int i = 0; i < n; i++){
            if(u.get(i) == i) cnt += 1;
        }
        return cnt - 1;
    }
};

128. 最长连续序列


class Solution {
public:
    class UnionSet {
    public:
        int *fa, *cnt, n;
        UnionSet(int n): n(n){
            fa = new int[n + 1];
            cnt = new int[n + 1];
            for(int i = 0;i <= n; i++){
                fa[i] = i;
                cnt[i] = 1;
            }
        }
        int get(int x){
            return fa[x] = (fa[x] == x ? x : get(fa[x]));
        }
        void merge(int a, int b){
            if(get(a) == get(b)) return;
            cnt[get(b)] += cnt[get(a)];
            fa[get(a)] = get(b);
            return;
        }
    };

    int longestConsecutive(vector<int>& nums) {
        unordered_map<int, int> ind;
        UnionSet u(nums.size());
        for(int i = 0; i < nums.size(); i++){
            int x = nums[i];
            if(ind.find(x) != ind.end()) continue;
            if(ind.find(x - 1) != ind.end()){
                u.merge(i, ind[x - 1]);
            }
            if(ind.find(x + 1) != ind.end()){
                u.merge(i, ind[x + 1]);
            }
            ind[x] = i;
         
        }
        int ans = 0;
        for(int i = 0;i < nums.size(); i++){
            if(u.get(i) == i && u.cnt[i] > ans) ans = u.cnt[i];
        }
        return ans;
    }
};

947. 移除最多的同行或同列石头

class Solution {
public:
    class UnionSet {
    public:
        int *fa, n;
        UnionSet(int n): n(n){
            fa = new int[n + 1];
            for(int i = 0;i <= n; i++){
                fa[i] = i;
            }
        }
        int get(int x){
            return fa[x] = (fa[x] == x ? x : get(fa[x]));
        }
        void merge(int a, int b){
            fa[get(a)] = get(b);
        }
    };

    int removeStones(vector<vector<int>>& stones) {
        UnionSet u(stones.size());
        unordered_map<int, int> ind_x, ind_y;
        for(int i = 0; i < stones.size(); i++){
            int x = stones[i][0];
            int y = stones[i][1];
            if(ind_x.find(x) != ind_x.end()){
                u.merge(i, ind_x[x]);
            }
            if(ind_y.find(y) != ind_y.end()){
                u.merge(i, ind_y[y]);
            }
            ind_x[x] = i;
            ind_y[y] = i;
        }
        int cnt = 0;
        for(int i = 0; i < stones.size(); i++){
            if(u.get(i) == i) cnt += 1;
        }
        return stones.size() - cnt;
    }
};

1202. 交换字符串中的元素

class Solution {
public:
    class UnionSet {
    public:
        int *fa, n;
        UnionSet(int n): n(n){
            fa = new int[n + 1];
            for(int i = 0;i <= n; i++){
                fa[i] = i;
            }
        }
        int get(int x){
            return fa[x] = (fa[x] == x ? x : get(fa[x]));
        }
        void merge(int a, int b){
            fa[get(a)] = get(b);
        }
    };

    string smallestStringWithSwaps(string s, vector<vector<int>>& pairs) {
        UnionSet u(s.size());
        priority_queue<char, vector<char>, greater<char>> h[s.size()];
        for(auto p: pairs){
            int i = p[0];
            int j = p[1];
            u.merge(i, j);
        }
        for(int i = 0; i< s.size(); i++ ){
            h[u.get(i)].push(s[i]);
        }
        string ret = "";
        for(int i = 0; i < s.size(); i++){
            ret += h[u.get(i)].top();
            h[u.get(i)].pop();
        }
        return ret;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值