并查集问题指的是告诉你许多组两点之间的连接状态,要求某两点是否相连或者求有多少个连通图。
这种问题的求解是比较模板化的,关键在于建立find函数和union函数。find函数的主要作用是找到一个节点的根节点,union函数的作用是将某两个点的根节点相连。
主要的解题步骤如下:
- 建立一个长度等于节点数的root数组和rank数组,root[i]=i,rank[i]=1.
- 创建一个递归find函数,当x==root[x]时返回x,否则就root[x]=find(root[x]),同时改变rank数组rank[x]=rank[root[x]]+1;
- 创建union(x,y)函数,当find(x)!=find(y),如果将秩小的一边连在秩大的一边上。
省份数量
这题除了上述的解题步骤以外,需要注意的就是邻接矩阵实际上只有对角线以上(不包括对角线)的部分是有用的,所以遍历的时候只要遍历这部分就行了。
public class Solution {
int[] root;
int[] rank;
public int FindCircleNum(int[][] isConnected) {
root=new int[isConnected.Length];
rank=new int[isConnected.Length];
for(int i=0;i<isConnected.Length;i++)
{root[i]=i;rank[i]=1;}
for(int i=0;i<isConnected.Length-1;i++)
for(int j=i+1;j<isConnected.Length;j++)//只遍历邻接矩阵的上三角
if(isConnected[i][j]==1)
union(i,j);
Hashtable hash=new Hashtable();
int res=0;
for(int i=0;i<isConnected.Length;i++)
{
int temp=find(i);
if(!hash.ContainsKey(temp))
{
hash.Add(temp,null);
res++;
}
}
return res;
}
public int find(int x)
{
if(x==root[x])
return x;
root[x]=find(root[x]);
rank[x]=rank[root[x]]+1;
return root[x];
}
public void union(int x,int y)
{
int rootX=find(x);
int rootY=find(y);
if(rootX!=rootY)
{
if(rank[rootX]>=rank[rootY])
root[rootY]=rootX;
else
root[rootX]=rootY;
}
}
}
朋友圈
现在有 10^5 个用户,编号为 1- 10^5,现在已知有 m 对关系,每一对关系给你两个数 x 和 y ,代表编号为 x 的用户和编号为 y 的用户是在一个圈子中,例如: A 和 B 在一个圈子中, B 和 C 在一个圈子中,那么 A , B , C 就在一个圈子中。现在想知道最多的一个圈子内有多少个用户。
这题比较麻烦的地方在于用户的编号并不一定是稠密排列的,所以root和rank数组只能用哈希表来表示,并且在寻找最大值时需要用到Keys和Values属性来遍历,此外,在利用这两个属性进行遍历时由于要用find函数,会改变哈希表本身,这样会报错,所以还需要一个额外的哈希表来存储有哪些用户编号。
using System;
using System.Collections;
using System.Collections.Generic;
class binchaji
{
Dictionary<int, int> root;
Dictionary<int, int> rank;
public void maxC(int n)
{
root = new Dictionary<int, int>();
rank = new Dictionary<int, int>();
Hashtable hash = new Hashtable();
while (n > 0)
{
string str = Console.ReadLine();
int x = int.Parse(str.Split(' ')[0]);
int y = int.Parse(str.Split(' ')[1]);
if (!root.ContainsKey(x))
{ root.Add(x, x); rank.Add(x, 1); hash.Add(x, null); }
if (!root.ContainsKey(y))
{ root.Add(y, y); rank.Add(y, 1); hash.Add(y, null); }
union(x, y);
n--;
}
Dictionary<int, int> max = new Dictionary<int, int>();
ICollection keys = hash.Keys;
foreach(int key in keys)
{
if (!max.ContainsKey(find(key)))
max.Add(root[key], 1);
else
max[root[key]]++;
}
int MAX = 0;
ICollection values = max.Values;
foreach (int value in values)
{
if (MAX < value)
MAX = value;
}
Console.WriteLine(MAX);
}
public int find(int x)
{
if (x == root[x])
return x;
root[x] = find(root[x]);
rank[x] = rank[root[x]] + 1;
return root[x];
}
public void union(int x, int y)
{
int rootX = find(x);
int rootY = find(y);
if (rootX != rootY)
{
if (rank[rootX] >= rank[rootY])
root[rootY] = rootX;
else
root[rootX] = rootY;
}
}
static void Main()
{
binchaji b = new binchaji();
int t = int.Parse(Console.ReadLine());
while (t > 0)
{
int n = int.Parse(Console.ReadLine());
b.maxC(n);
t--;
}
}
}