并查集

11 篇文章 0 订阅
3 篇文章 0 订阅

在计算机科学中,并查集是一种树型的数据结构,用于处理一些不交集(Disjoint Sets)的合并及查询问题。有一个联合-查找算法(Union-find Algorithm)定义了两个用于此数据结构的操作:

Find:确定元素属于哪一个子集。它可以被用来确定两个元素是否属于同一子集。
Union:将两个子集合并成同一个集合。
由于支持这两种操作,一个不相交集也常被称为联合-查找数据结构(Union-find Data Structure)或合并-查找集合(Merge-find Set)。
为了更加精确的定义这些方法,需要定义如何表示集合。一种常用的策略是为每个集合选定一个固定的元素,称为代表,以表示整个集合。接着,Find(x)Find(x) 返回 xx 所属集合的代表,而 Union 使用两个集合的代表作为参数。

目录

一、并查集介绍

 二、并查集案例

1. 无向环连接

2. Leetcode案例-省份连接案例


一、并查集介绍

并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中。其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。

并查集是一种树型的数据结构,用于处理一些不相交集合(disjoint sets)的合并及查询问题。常常在使用中以森林来表示。

 二、并查集案例

1. 无向环连接

给出一个二维数组,如vec[6][2]={{0,1},{1,2},{1,3},{2,4},{3,5},{4,5}},判断其中是否存在连通环(由多个节点组成的连通区域,其中每个节点都经过n步后返回自身节点)。

如:

                        图1                                                             图2

上图1中{1,2,3,4,5}组成一个环,即存在联通环;而图2中无连通区域。

下面给出该代码:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

class Solution{
private:
	/* 寻找每一个节点的父节点函数*/
	int find_root(vector<int>& parent, int x){
		int x_root = x;
		/*如果每个节点指向本身,则自身为父节点;如果自己指向另一个节点,则父节点为其他节点,依次迭代,找到最高的父节点*/ 
		while(parent[x_root] != x_root){
			x_root = parent[x_root];
		}
		/* 找到父节点后,返回父节点的标号*/ 
		return x_root;
	}
	
	/* 联合函数,如果两个节点x和y的父节点不相同,则可以将两个父节点联合起来;如果父节点相同,则说明两个连点在一个连通域中,无需改变*/
	/* 输出:0-表示两个节点处于一个连通域中,无需进行联合;1-两个节点处于不同的连通域中,并将父节点进行联合*/ 
	int union_join(vector<int>& parent, int x, int y,vector<int>& rank){
		/* 先找到x y节点的父节点,然后判断两个节点是否在一个连通域中*/ 
		int x_root = find_root(parent,x);
		int y_root = find_root(parent,y);
		cout << x_root << " " << y_root << endl;
		/* 如果两个节点不在一个连通域中,则进行联合,联合方向随意(但是可能存在一个最长路径问题,后面会解决)*/ 
		if(x_root != y_root){
			if(rank[x_root] > rank[y_root]){
				parent[y_root] = x_root;
			}else{
				parent[x_root] = y_root;
			}
			return 1;
		}else{
			return 0;
		}
	}
	
public:
	// 寻找联通区域 
	int findCircleNum(vector<vector<int> >& vec){
		int n = vec.size();
		cout << n << endl; 
		/* 创建父节点目录,此时所有节点的父节点都为自己,指向-1*/
		vector<int> parent(6);
		/* rank表示每个节点的高度,为了使路径最短,应该将父节点表示为rank最大的元素*/
		vector<int> rank(6);
		// 此时需要将每个节点指向父节点(-1),n表示节点的数量 
		for(int i=0;i<6;i++){
			parent[i] = i;
			rank[i] = 0; 
			cout << parent[i] << " ";
		}
		cout << endl;
		for(int i=0;i<n;i++){
			if ( union_join(parent,vec[i][0],vec[i][1], rank) == 0 ) return 1;
		}
		return 0;
	}
	
};

int main(){
	vector<vector<int> > parent;
	parent.push_back(vector<int>());
	parent[0].push_back(0); 
	parent[0].push_back(1); 
	parent.push_back(vector<int>());
	parent[1].push_back(1); 
	parent[1].push_back(2); 
	parent.push_back(vector<int>());
	parent[2].push_back(1); 
	parent[2].push_back(3); 
	parent.push_back(vector<int>());
	parent[3].push_back(2); 
	parent[3].push_back(5); 
	parent.push_back(vector<int>());
	parent[4].push_back(3); 
	parent[4].push_back(4); 
	parent.push_back(vector<int>());
	parent[5].push_back(4); 
	parent[5].push_back(5); 
	
	Solution S;
	int x = S.findCircleNum(parent);
	if(x) cout << "Circle detected!" << endl;
	else cout << "Circle not detected!" << endl;
} 

2. Leetcode案例-省份连接案例

有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。

省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中 省份 的数量。


输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]]
输出:2
示例 2:


输入:isConnected = [[1,0,0],[0,1,0],[0,0,1]]
输出:3

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-provinces

class Solution {
public:
    int Find(vector<int>& parent, int index) {
        while(parent[index] != index){
            index = parent[index];
        }
        return parent[index];
    }

    void Union(vector<int>& parent, int index1, int index2) {
        parent[Find(parent, index1)] = Find(parent, index2);
    }

    int findCircleNum(vector<vector<int>>& isConnected) {
        int provinces = isConnected.size();
        vector<int> parent(provinces);
        for (int i = 0; i < provinces; i++) {
            parent[i] = i;
        }
        for (int i = 0; i < provinces; i++) {
            for (int j = i + 1; j < provinces; j++) {
                if (isConnected[i][j] == 1) {
                    Union(parent, i, j);
                }
            }
        }
        int circles = 0;
        for (int i = 0; i < provinces; i++) {
            if (parent[i] == i) {
                circles++;
            }
        }
        return circles;
    }
};


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值