LeetCode 947.移除最多的同行或同列石头 并查集/Medium


1.Description

n 块石头放置在二维平面中的一些整数坐标点上。每个坐标点上最多只能有一块石头。

如果一块石头的 同行或者同列 上有其他石头存在,那么就可以移除这块石头。

给你一个长度为 n 的数组 stones ,其中 stones[i] = [xi, yi] 表示第 i 块石头的位置,返回 可以移除的石子 的最大数量。


2.Example

输入:stones = [[0,0],[0,1],[1,0],[1,2],[2,1],[2,2]]
输出:5
解释:一种移除 5 块石头的方法如下所示:
1. 移除石头 [2,2] ,因为它和 [2,1] 同行。
2. 移除石头 [2,1] ,因为它和 [0,1] 同列。
3. 移除石头 [1,2] ,因为它和 [1,0] 同行。
4. 移除石头 [1,0] ,因为它和 [0,0] 同列。
5. 移除石头 [0,1] ,因为它和 [0,0] 同行。
石头 [0,0] 不能移除,因为它没有与另一块石头同行/列。

输入:stones = [[0,0],[0,2],[1,1],[2,0],[2,2]]
输出:3
解释:一种移除 3 块石头的方法如下所示:
1. 移除石头 [2,2] ,因为它和 [2,0] 同行。
2. 移除石头 [2,0] ,因为它和 [0,0] 同列。
3. 移除石头 [0,2] ,因为它和 [0,0] 同行。
石头 [0,0][1,1] 不能移除,因为它们没有与另一块石头同行/列。


3.Solution

一定可以把一个连通图里的所有顶点根据这个规则删到只剩下一个顶点。最多可以移除的石头的个数 = 所有石头的个数 - 连通分量的个数。
使用了并查集,结合题解,这里主要解释一下代码中每一步的意思。(注释)

使用Map

**:这里的find和union是将所有连通的节点的x坐标和y坐标放到一个并查集里

import java.util.HashMap;
import java.util.Map;

public class Solution {

    public int removeStones(int[][] stones) {
        UnionFind unionFind = new UnionFind();

        for (int[] stone : stones) {
            // 下面这三种写法任选其一
            // unionFind.union(~stone[0], stone[1]);
            // unionFind.union(stone[0] - 10001, stone[1]);
            unionFind.union(stone[0] + 10001, stone[1]);
            //因为x,y坐标取值范围是0到10000,而在map中需要使用
            //get(key)的函数,key的值可能为x也可能为y,因此需确保x和y的值不一样。
        }
        return stones.length - unionFind.getCount();
    }

    private class UnionFind {

        private Map<Integer, Integer> parent;
        private int count;//连通分量的数量

        public UnionFind() {
            this.parent = new HashMap<>();
            this.count = 0;
        }

        public int getCount() {
            return count;
        }

        public int find(int x) {
            if (!parent.containsKey(x)) {
                parent.put(x, x);
                // 并查集集中新加入一个结点,结点的父亲结点是它自己
                //所以连通分量的总数 +1
                count++;
            }

            if (x != parent.get(x)) {
                parent.put(x, find(parent.get(x)));
                //找到根节点,因为put会刷新,所以递归结束后结点都是直接指向根节点,
                //也就是说并查集只有两层,find递归一次就找到根节点。
            }
            return parent.get(x);
        }

        public void union(int x, int y) {//因为将x和y的值平等的看做集合里的点了,所以可以直接合并了
            int rootX = find(x);
            int rootY = find(y);
            if (rootX == rootY) {//根节点相同,说明在同一个集合里了
                return;
            }

            parent.put(rootX, rootY);//将X根节点接到Y根节点后
            // 两个连通分量合并成为一个,连通分量的总数 -1
            count--;
        }
    }
}

使用数组

class Solution {
    int p[] = new int[20009];
	public int removeStones(int[][] stones) {
		int res = 0;
		for(int i=0;i<p.length;i++) {
			p[i] = i;
		}
		for(int i=0;i<stones.length;i++) {
			union(stones[i][0],stones[i][1]+10000);
		}
		Set<Integer> set  = new HashSet<>();
		for(int i=0;i<stones.length;i++) {
				set.add(find(stones[i][0]));
		}
		return stones.length-set.size();
	}
	private int find(int i) {
		// TODO Auto-generated method stub
		if(p[i]!=i) {
			p[i] = find(p[i]);
		}
		return p[i];
	}
	private void union(int i, int j) {
		// TODO Auto-generated method stub
		p[find(i)] = find(j);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值