美文网首页
947. 移除最多的同行或同列的石头(Python)

947. 移除最多的同行或同列的石头(Python)

作者: 玖月晴 | 来源:发表于2021-04-07 11:17 被阅读0次

难度:★★★☆☆
类型:图
方法:深度优先搜素,并查集

题目

力扣链接请移步本题传送门
更多力扣中等题的解决方案请移步力扣中等题目录

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

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

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

示例 1:

输入: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] 不能移除,因为它没有与另一块石头同行/列。

示例 2:

输入: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:

输入:stones = [[0,0]]
输出:0
解释:[0,0] 是平面上唯一一块石头,所以不可以移除它。

提示:

1 <= stones.length <= 1000
0 <= xi, yi <= 104
不会有两块石头放在同一个坐标点上

解答

基于对问题的分析,有下面的一些定义需要留意:

第一,连接的定义。如果两个石头在同一行或者同一列,我们就说这两个石头是相互连接的;

第二,连接的传递性。如果石头A和石头B存在连接关系,且石头B和石头C存在连接关系,则石头A和石头C存在连接关系。

第三,连通域。我们把存在连接关系的若干结点组成的集合叫做一个连通域。(换言之,对于连通域中的所有石头,我们总可以通过调整移除石头的顺序,实现最后只剩下一块石头,这是本题的题眼)。

第四,问题的定义。我们把石头看做一个个结点,根据石头之间的连接关系可以构建出一张图,这道题实际上就是一个图的搜索问题。问题就是统计图中连通域的个数。

常用的是深度优先搜索或并查集算法。

方法1:深度优先搜索

深度优先搜索通过代码上的递归或栈实现逻辑上的连通域搜寻。

【建图】

我们给每个石头按照下标编号,然后使用一个字典来存储每个石头连接的石头的编号。例如对于石头i,其对应的值是由石头i所有存在连接关系的石头组成的列表。

【搜索】

使用递归方式实现搜索。需要注意的是,在搜索之前,先使用vis集合存储已经被查看过的石头,每次遍历到某个石头,则将该石头放进vis中。

【统计】

从第一个石头开始遍历,将各个石头所在连通域中所有石头放进集合,每次遍历到新的连通域,计数器num加一。需要注意跳过已经被查看过的石头。

【返回值】

最后返回石头总数n减去连通域的个数num。

from collections import defaultdict


class Solution:
    def removeStones(self, stones):

        n = len(stones)
        graph = defaultdict(set)
        for i, (x1, y1) in enumerate(stones):
            for j, (x2, y2) in enumerate(stones):
                if i != j and (x1 == x2 or y1 == y2):
                    graph[i].add(j)
                    graph[j].add(i)

        def dfs(i):
            vis.add(i)
            for y in graph[i]:
                if y not in vis:
                    dfs(y)

        vis = set()
        num = 0
        for i in range(n):
            if i not in vis:
                num += 1
                dfs(i)

        return n - num

方法2:并查集

并查集的功能在于,将同一个连通域中的所有结点挂在同一根节点下,对于快速统计连通域的数目是非常方便的。

并查集包含几个主要方法:

【初始化】

一般情况下,并查集以哈希表的形式存在(列表由于索引关系的存在,本质也是哈希表),表达的是每个结点与其根节点的对应关系。初始情况下,将每个结点的根节点初始化为本身。

【连通域的合并】

将两个连通域合并成为一个大连通域,可以选取两个连通域中各自任意两个结点,将它们的根节点建立连接即可。

【查找根节点】

通过递归调用的方法可以找到根节点,在遍历过程中,将子节点的父节点改成根节点,大大减少计算开销。

为了便于理解,我们先把石头而非石头的坐标作为对象来研究。

我们将石头两两研究,如果存在连接关系,则将这两个石头所在连通域合并。最后通过统计根节点的数目来反映连通域的数目。

class UFS:
    def __init__(self, stones):
        self.dct = {stone: stone for stone in stones}

    def find(self, stone):
        if self.dct[stone] != stone:
            self.dct[stone] = self.find(self.dct[stone])
        return self.dct[stone]

    def union(self, stone_1, stone_2):
        root_1 = self.find(stone_1)
        root_2 = self.find(stone_2)
        self.dct[root_1] = root_2


class Solution(object):
    def removeStones(self, stones):
        stones = list(map(tuple, stones))
        ufs = UFS(stones)

        def same_row_or_column(stone_1, stone_2):
            (x1, y1), (x2, y2) = stone_1, stone_2
            return x1 == x2 or y1 == y2

        for stone_1 in stones:
            for stone_2 in stones:
                if same_row_or_column(stone_1, stone_2):
                    ufs.union(stone_1, stone_2)
        return len(stones) - len({ufs.find(stone) for stone in stones})

还有一种方法,即将石头位置的横纵坐标降维,合并到一个列表中,为了使得他们不干扰,纵坐标从10000开始计数(因为横坐标的最大值是10000)。这样做的好处是,省去了判断两个石头是否连接的过程,因为在并查集的内部就已经做了判断。

class UFS:
    def __init__(self):
        self.p = []

    def find(self, x):
        if self.p[x] != x:
            self.p[x] = self.find(self.p[x])
        return self.p[x]

    def union(self, x, y):
        xr = self.find(x)
        yr = self.find(y)
        self.p[xr] = yr


class Solution(object):
    def removeStones(self, stones):
        ufs = UFS()
        for x, y in stones:
            ufs.union(x, y + 10000)
        return len(stones) - len({ufs.find(x) for x, y in stones})

如有疑问或建议,欢迎评论区留言~

有关更多力扣中等题的python解决方案,请移步力扣中等题解析

相关文章

  • 947. 移除最多的同行或同列石头

    lzyprime 博客 (github)[https://lzyprime.github.io] 创建时间:2...

  • 947. 移除最多的同行或同列的石头(Python)

    难度:★★★☆☆类型:图方法:深度优先搜素,并查集 题目 力扣链接请移步本题传送门[https://leetcod...

  • Leetcode 947 Most Stones Removed

    1. 数岛屿问题 如果两个石头在同行或者同列,两个石头就是连接的。连在一起的石头,可以组成一个连通图。每一个连通图...

  • javaScript数据结构--散列表

    散列集合是由一个集合构成,但是插入、移除、或获取元素时,使用的是散列函数 散列表的代码实现 // 散列表 func...

  • 2018-07-06-Python strip()方法

    Python strip() 方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列。 不能删除中间部分...

  • 移除心中的石头(71)

    一直以来,孩子的学习问题一直是负在我心中的一块大石头,每每提到孩子或者仅仅是想到孩子的时候,心里都有沉甸甸的感觉。...

  • Python strip()方法

    描述 Python strip() 方法用于移除字符串头尾指定的字符(默认为空格)或字符序列。 注意:该方法只能删...

  • 回溯算法解数独问题的一种思路

    核⼼思路,就是对每⼀个空着的格⼦穷举 1 到 9,如果遇到不合法的数字(在同⼀⾏或同⼀列或同⼀个 3×3 的区域中...

  • strip()方法

    Python strip() 方法用于移除字符串头尾指定的字符(默认为空格)或字符序列。 注意:该方法只能删除开头...

  • Python列表最常见的问题【总结】

    列表是Python中使用最多的一种数据结果,如何高效操作列表是提高代码运行效率的关键,本文总结了一些python列...

网友评论

      本文标题:947. 移除最多的同行或同列的石头(Python)

      本文链接:https://www.haomeiwen.com/subject/ijloqltx.html