1296. 划分数组为连续数字的集合(贪心算法)

给你一个整数数组 nums 和一个正整数 k,请你判断是否可以把这个数组划分成一些由 k 个连续数字组成的集合。
如果可以,请返回 True;否则,返回 False。 

示例 1:

输入:nums = [1,2,3,3,4,4,5,6], k = 4
输出:true
解释:数组可以分成 [1,2,3,4] 和 [3,4,5,6]。
示例 2:

输入:nums = [3,2,1,2,3,4,3,4,5,9,10,11], k = 3
输出:true
解释:数组可以分成 [1,2,3] , [2,3,4] , [3,4,5] 和 [9,10,11]。
示例 3:

输入:nums = [3,3,2,2,1,1], k = 3
输出:true
示例 4:

输入:nums = [1,2,3,4], k = 3
输出:false
解释:数组不能分成几个大小为 3 的子数组。

我们首先考虑数组 nums 中最小的数 a0,显然 a0 一定是某个集合的最小值,且这个集合包含 [a0, a0 + 1, ..., a0 + k - 1] 共 k 个数,因此我们划分出了一个集合,可以将这 k 个数从 nums 中移除。我们接着考虑数组 nums 剩余元素中最小的数 a1,同理 a1 也是某个集合的最小值,且这个集合包含 [a1, a1 + 1, ..., a1 + k - 1] 共 k 个数,我们同样划分出了一个集合,并将这 k 个数从 nums 中移除。我们重复这样的操作,直到 nums 中的所有数都被移除(返回 True) 或在某一步最小值为 ai 时,数组 nums 的剩余元素并不包含 [ai, ai + 1, ..., ai + k - 1] 这 k 个数(返回 False)。

那么应当使用什么数据结构来实现这个算法呢?由于我们需要从小到大地考虑数组 nums 中的数,并且需要查询 nums 剩余元素中是否包含某些数,因此我们可以使用有序映射(TreeMap)来存储 nums 中所有的数。有序映射中的每个键值对 (num, occ) 表示数 num 在数组 nums 中出现了 occ 次。由于我们使用的映射(Map)是有序的,因此我们可以按照键的顺序从小到大遍历这些键值对。当我们遍历到键值对 (num, occ) 时,即表示数组 nums 剩余元素中最小的数为 num,且出现了 occ 次。此时我们依次查询 num + 1, num + 2, ..., num + k - 1 出现的次数是否均不少于 occ,如果是,那么我们就得到了 occ 个集合 [num, num + 1, ..., num + k - 1],并把这些元素全部移除;如果不是,那么我们就无法将剩余的元素划分成一些由 k 个连续数字组成的集合。

注意:由于 Python 语言中不提供有序映射,因此我们需要先使用无序映射(即哈希映射,例如 Python 中的 dict 或 collections.Counter),再将无序映射中的键进行排序,通过遍历排序后的键来达到从小到大遍历键值对的效果。

class Solution:
    def isPossibleDivide(self, nums: List[int], k: int) -> bool:
        s = collections.Counter(nums)
        ordered_nums = sorted(s)
        for num in ordered_nums:
            occ = s[num]
            if s[num] > 0:
                for i in range(num + 1, num + k):
                    if s[i] >= occ:
                        s[i] -= occ
                    else:
                        return False
        return True

链接:https://leetcode-cn.com/problems/divide-array-in-sets-of-k-consecutive-numbers/solution/hua-fen-shu-zu-wei-lian-xu-shu-zi-de-ji-he-by-le-2/

posted @ 2020-05-22 09:26  USTC丶ZCC  阅读(415)  评论(0编辑  收藏  举报