Leetcode-划分数组为连续数字的集合(python)

题目

给你一个整数数组 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 的子数组。

提示:
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^9
1 <= k <= nums.length

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/divide-array-in-sets-of-k-consecutive-numbers

思路

要将n个数字排列为 n//k 组 k 个连续数字的集合,那么有一些基本的条件需要满足:

  1. n >= k
  2. n%k==0 (n必须是k的整数倍)

下面针对不同的解法讲解不同的思路:

解法1

思路: 这个问题可以想象成将已排序数组中的每个元素放入n//k个容器中, 这n//k个容器的容量均为k. 每次放元素时, 从一个容器开始遍历寻找合适的位置,
如果当前容器满足以下两个条件中的一个就可以放入:

  1. 容器是空的
  2. 容器中元素<k,并且容器中最大的元素比当前元素小1.
    如果不满足, 判断第二个容器…, 直到遍历完n//k个容器,
    当某个元素遍历完所有容器都没有合适的位置可以存放时,说明这个数组不满足要求.

代码说明: 由于容器数量已知, 且都是连续整数, 使用中只需要记录每个容器当前的最大值和每个容器当前的元素个数就可以了,代码中使用两个数组来存储

复杂度说明 : 算法的时间复杂度为O(n*(n//k)), 空间复杂度为O(2*n//k), 在leetcode中超时了, 思路应该是对的,就记录下来了

class Solution(object):
    def isPossibleDivide(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: bool
        """
        # 数字的数量不是k的倍数或者数量小于k是不行的
        if len(nums)%k!=0 or k>len(nums):
            return False

        nlist = len(nums)//k
        maxnum = [0 for i in range(nlist)]
        n_num = maxnum[:]

        # 元素排序
        s_nums = sorted(nums)

        for num in s_nums:
            flag, i = 1, 0
            while(i<nlist and flag):
                # 如果这个list为空
                if n_num[i]==0:
                    maxnum[i]=num
                    n_num[i]+=1
                    flag = 0
                elif n_num[i]<k and num-maxnum[i]==1:
                    maxnum[i]=num
                    n_num[i]+=1
                    flag = 0
                else: 
                    i += 1
                    flag = 1
            # flag 仍然等于1, 说明当前num没有合适的位置可以放
            if flag:
                return False
        return True

解法2

思路: 使用字典存储每个元素出现的次数, 然后从最小的元素开始遍历,例如{1:100, 2:100, 3:100, 4:1, 5:1, 6:1} , 表示1出现了100次, 2出现了100次, 3出现了100次,…, 如果k==3, 那么当1有100次的时候, 要能够组成连续的整数序列, 2和3至少出现了100次, 那么在遍历的到1的时候可以让1~3的次数都减去1的出现的次数.
按照这样的规律,对字典中的key按照从小到大的顺序进行遍历,减去相应的次数.

复杂度分析 : 需要新建字典来存储元素出现的次数 , 最好的情况为O(1), 最坏的情况为O(n) (常用的空间复杂度有O(1), O(n), O(n^2))

class Solution(object):
    def isPossibleDivide(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: bool
        """
        # 数字的数量不是k的倍数或者数量小于k是不行的
        if len(nums) % k != 0 or k > len(nums):
            return False
            
        times = {}
        # 将所有元素无重复地排序存储
        uninum = sorted(list(set(nums)))

        # 统计每个数字出现的次数: 这样写不超时
        # 在加入键的时候,键不会按照加入的顺序排列
        times = {}
        for i in nums:
            if times.__contains__(i):
                times[i]+=1
            else:
                times[i]=1	
                
        # 统计每个数字出现的次数: 这样写会超时, 
        # for i in uninum:
        #   times[i] = nums.count(i)

        for key in uninum:
            # 如果还包含这个键
            if times[key]==0: continue
            if times[key]<0: return False
            tmp = times[key]
            for j in range(key, key + k):
                if not times.__contains__(j) or times[j] < tmp:
                    return False
                else:
                    times[j] = times[j] - tmp
        return True
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值