并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受。即使在空间上勉强通过,运行的时间复杂度也极高,只能用并查集来描述。故并查集是一种树型的数据结构,可用于处理一些不相交集合(disjoint sets)的合并及查询问题。
并查集(Union-find Data Structure)是一种树型的数据结构。它的特点是由子结点找到父亲结点,用于处理一些不交集(Disjoint Sets)的合并及查询问题。并查集的思想是用一个数组表示了整片森林(parent),树的根节点唯一标识了一个集合,我们只要找到了某个元素的的树根节点,就能确定它在哪个集合里。 并查集跟树有些类似,只不过它和树是相反的。在树这个数据结构里面,每个节点会记录它的子节点。而在并查集里,每个节点只会记录它的父节点。
并查集的代码实现:
class UnionFind {
private Map father;
public UnionFind() {
father = new HashMap();
}
public void add(int x) {
if (!father.containsKey(x)) {
father.put(x, null);
}
}
public void merge(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX != rootY){
father.put(rootX,rootY);
}
}
public int find(int x) {
int root = x;
while(father.get(root) != null){
root = father.get(root);
}
while(x != root){
int original_father = father.get(x);
father.put(x,root);
x = original_father;
}
return root;
}
public boolean isConnected(int x, int y) {
return find(x) == find(y);
}
}

题目解析:建立一个并查集UF对象,用map存储,并用head指向共同的头结点,只有当head为空的时候其中size才有意义。
代码如下:
/**
* 并查集
*/
class Solution {
public int longestConsecutive(int[] nums) {
if(nums.length==0) return 0;
int max=1;
Map map = new HashMap<>();
for(int i : nums){
if(map.containsKey(i)) continue;
if(!map.containsKey(i)){
map.put(i,new UF(null,1));
}
if(map.containsKey(i+1)){
UF head = map.get(i+1);
int size = ++head.size;
max=Math.max(size,max);
UF item=new UF(null,size);
head.head=item;
map.put(i,item);
}
if(map.containsKey(i-1)){
UF head = map.get(i-1);
while(head.head!=null){
head=head.head;
}
head.size=map.get(i).size+head.size;
max=Math.max(head.size,max);
map.get(i).head=head;
}
}
return max;
}
}
class UF{
public UF head;
public int size;
public UF (UF head , int size ){
this.head=head;
this.size=size;
}
}

题目解析:使用Union-Find法,先将四条边的’O’和dummy连接 --> 内部的’O’都与之四个邻居的’O’连接。最后不和dummy连接的’O’都是要修改为’X’的位置,修改即可。
代码如下:
/**
* 并查集
*/
class Solution {
public void solve(char[][] board) {
// 注意二维数组的坐标[i,j]换算成一个数:i*n+j
int m = board.length;
int n = board[0].length;
UF uf = new UF(m*n+1);
// 最后一个位置留给dummy用
int dummy = m*n;
for(int i=0;i
题目解析:我们可以用并查集代替深搜和广搜搜索,为了求出岛屿的数量,我们可以扫描整个二维网格。如果一个位置为1,则将其与相邻四个方向上的1在并查集中进行合并。最终岛屿的数量就是并查集中连通分量的数目。
代码如下:
/**
* 并查集
*/
class Solution {
class UnionFind {
int count;
int[] parent;
int[] rank;
public UnionFind(char[][] grid) {
count = 0;
int m = grid.length;
int n = grid[0].length;
parent = new int[m * n];
rank = new int[m * n];
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (grid[i][j] == '1') {
parent[i * n + j] = i * n + j;
++count;
}
rank[i * n + j] = 0;
}
}
}
public int find(int i) {
if (parent[i] != i) parent[i] = find(parent[i]);
return parent[i];
}
public void union(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] += 1;
}
--count;
}
}
public int getCount() {
return count;
}
}
public int numIslands(char[][] grid) {
if (grid == null || grid.length == 0) {
return 0;
}
int nr = grid.length;
int nc = grid[0].length;
int num_islands = 0;
UnionFind uf = new UnionFind(grid);
for (int r = 0; r < nr; ++r) {
for (int c = 0; c < nc; ++c) {
if (grid[r][c] == '1') {
grid[r][c] = '0';
if (r - 1 >= 0 && grid[r-1][c] == '1') {
uf.union(r * nc + c, (r-1) * nc + c);
}
if (r + 1 < nr && grid[r+1][c] == '1') {
uf.union(r * nc + c, (r+1) * nc + c);
}
if (c - 1 >= 0 && grid[r][c-1] == '1') {
uf.union(r * nc + c, r * nc + c - 1);
}
if (c + 1 < nc && grid[r][c+1] == '1') {
uf.union(r * nc + c, r * nc + c + 1);
}
}
}
}
return uf.getCount();
}
}
《算法系列》之模拟