Leetcode 947 Most Stones Removed with Same Row or Column

1. 数岛屿问题

如果两个石头在同行或者同列,两个石头就是连接的。连在一起的石头,可以组成一个连通图。每一个连通图至少会剩下1个石头。
所以我们希望存在一种思路,每个连通图都只剩下1个石头。
这样这题就转化成了数岛屿的问题。

2. 朴素DFS

实际上做这道题,你并不需要去构造或者证明,移走石头的方案。有了step1里的思路,已经足够用dfs来解决这个问题了。

    def removeStones(self, points):
        rows = collections.defaultdict(set)
        cols = collections.defaultdict(set)
        for i, j in points:
            rows[i].add(j)
            cols[j].add(i)

        def dfsRow(i):
            seenR.add(i)
            for j in rows[i]:
                if j not in seenC:
                    dfsCol(j)

        def dfsCol(j):
            seenC.add(j)
            for i in cols[j]:
                if i not in seenR:
                    dfsRow(i)

        seenR, seenC = set(), set()
        islands = 0
        for i, j in points:
            if i not in seenR:
                islands += 1
                dfsRow(i)
                dfsCol(j)
        return len(points) - islands

3. 优化行列处理

上述代码有很多冗余,实际上我们对行列的搜索,没有任何本质区别。
只不过是因为同一个index,可能是行也可能是列,所以我们做了区分。
实际上,只要我们能区分行列的index,代码就可以缩减一半了。

行的index我们还可以用0~N - 1,列的index我们使用N~2N-1就可以了。

    def removeStones(self, points):
        index = collections.defaultdict(set)
        for i, j in points:
            index[i].add(j + 10000)
            index[j + 10000].add(i)

        def dfs(i):
            seen.add(i)
            for j in index[i]:
                if j not in seen:
                    dfs(j)

        seen = set()
        islands = 0
        for i, j in points:
            if i not in seen:
                islands += 1
                dfs(i)
                dfs(j + 10000)
        return len(points) - islands

4. 换个思考角度

我们之前的思路是,对石头所在point进行搜索。
有了以上代码,我们就会发现,其实我们搜索的元素是行列的index。

我们以为是行列把石头连接在了一起。
换个角度思考,也可以是一个石头把它所在行列坐标连在了一起。

我们的输入是所有的石头,每个石头都固定连接两个元素。
想到这里,union find的思路就已经呼之欲出了。

C++:

    int removeStones(vector>& stones) {
        for (int i = 0; i < stones.size(); ++i)
            uni(stones[i][0], ~stones[i][1]);
        return stones.size() - islands;
    }

    unordered_map f;
    int islands = 0;

    int find(int x) {
        if (!f.count(x)) f[x] = x, islands++;
        if (x != f[x]) f[x] = find(f[x]);
        return f[x];
    }

    void uni(int x, int y) {
        x = find(x), y = find(y);
        if (x != y) f[x] = y, islands--;
    }

Java:

    Map f = new HashMap<>();
    int islands = 0;

    public int removeStones(int[][] stones) {
        for (int i = 0; i < stones.length; ++i)
            union(stones[i][0], ~stones[i][1]);
        return stones.length - islands;
    }

    public int find(int x) {
        if (f.putIfAbsent(x, x) == null)
            islands++;
        if (x != f.get(x))
            f.put(x, find(f.get(x)));
        return f.get(x);
    }

    public void union(int x, int y) {
        x = find(x);
        y = find(y);
        if (f.get(x) != y) {
            f.put(find(x), find(y));
            islands--;
        }
    }

Python:

    def removeStones(self, points):
        UF = {}
        def find(x):
            if x != UF[x]:
                UF[x] = find(UF[x])
            return UF[x]
        def union(x, y):
            UF.setdefault(x, x)
            UF.setdefault(y, y)
            UF[find(x)] = find(y)

        for i, j in points:
            union(i, ~j)
        return len(points) - len({find(x) for x in UF})

5. 构造移走石头方案

逆向思维,来看一下,如果开始只有一个石头,每次都只能放一个新石头在同行或同列,我们能否构造出整个岛屿。
还是不难得,可以用dfs里查找石头的顺序放,就可以构造整个岛屿了。
反之,按照反序也可以移走整个岛屿直到剩下一个石头。

你可能感兴趣的:(Leetcode 947 Most Stones Removed with Same Row or Column)