https://leetcode.com/problems/most-stones-removed-with-same-row-or-column/
通过这道例题来复习理解Union Find算法
碰到棋盘类型的题目不知如何下手的情况下都可以想想是不是符合Union Find的阶梯逻辑呢;
On a 2D plane, we place stones at some integer coordinate points.
Each coordinate point may have at most one stone.
Now, a move consists of removing a stone that shares a column
or row with another stone on the grid.
What is the largest possible number of moves we can make?
Example 1:
Input: stones = [[0,0],[0,1],[1,0],[1,2],[2,1],[2,2]]
Output: 5
Example 2:
Input: stones = [[0,0],[0,2],[1,1],[2,0],[2,2]]
Output: 3
Example 3:
Input: stones = [[0,0]]
Output: 0
Note:
1 <= stones.length <= 1000
0 <= stones[i][j] < 10000
先不说这道题如何使用Union Find, 先来讲一下什么是Union Find;
Union Find是一种算法,根本逻辑是有很多nodes,现在呢我们要以某种方式将他们划分为不同的group。Union Find算法顾名思义有两个最重要的Function,其一是Union
其二是Find
。
FunctionFind
可以寻找到某个节点的父节点,子节点跟父节点的关系是他们都同属于一个group。最理想的效果是所有的在同一个group里的节点都指向共同的root根节点(实际算法中这个效果不会一直保持,否则会增加耗时)。当然也有可能碰到新节点,那么对于新节点而言自己就是自己的父节点,这也就是对新节点的默认操作。
FunctionUnion
的逻辑是宣告两个节点同属于一个group。在Union
方法中我们需要调用Find
方法找到两个节点当前的父节点,然后将他们合并。
在Union Find整个结构建好以后,我们可以对任意两个节点进行query,看他们是否属于同一个group,同时我们也能获取一共有多少个group存在。
好了,现在回到这道题,可以使用Union Find方法来完成。具体代码如下:
class Solution {
Map<Integer, Integer> map = new HashMap<>();
int island = 0;
public int removeStones(int[][] stones) {
if(stones.length <= 1){
return 0;
}
for(int i = 0; i < stones.length; i++){
union(stones[i][0], stones[i][1] + 10000);
}
return stones.length - island;
}
private int find(int i){
if(map.putIfAbsent(i,i) == null){
this.island ++;
return i; //对于新节点的默认操作
}
int j = i;
while(map.get(j) != j){ //假如节点并不是一个group的根节点
j = map.get(j); //那么我们query到它的时候就更新成group的根节点root
} //一个group的root的特点是它的父节点就是自己
map.put(i,j);
return j;
}
private void union(int i, int j){
i = find(i); //这里需要保证find找到的是group根节点,否则算法逻辑会出错
j = find(j);
if(i != j){ //不等证明证明i和j之前处于不同的group
island--;
map.put(j, i);
}
}
}