LeetCode-164. 最大间距【数组 桶排序 基数排序 排序】

本文介绍了如何使用桶排序和基数排序解决LeetCode中的最大间距问题,两种方法均能在线性时间内完成并在额外空间为线性的条件下找到数组排序后相邻元素的最大差值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:

给定一个无序的数组 nums,返回 数组在排序之后,相邻元素之间最大的差值 。如果数组元素个数小于 2,则返回 0 。

您必须编写一个在「线性时间」内运行并使用「线性额外空间」的算法。

示例 1:
输入: nums = [3,6,9,1]
输出: 3
解释: 排序后的数组是 [1,3,6,9], 其中相邻元素 (3,6) 和 (6,9) 之间都存在最大差值 3。

示例 2:
输入: nums = [10]
输出: 0
解释: 数组元素个数小于 2,因此返回 0。

提示:
1 <= nums.length <= 105
0 <= nums[i] <= 109

解题思路一:桶排序,这种方法比较简单,记住3个公式即可。如下:

难度在于两个线性。

  1. 首先:我们期望将数组中的各个数等距离分配,也就是每个桶的长度相同,也就是对于所有桶来说,桶内最大值减去桶内最小值都是一样的。
    每个桶的长度 = max ⁡ ( 1 , ⌊ max ⁡ ( n u m s ) − min ⁡ ( n u m s ) l e n ( n u m s ) − 1 ⌋ ) \max(1,\lfloor{{\max(nums) - \min(nums)} \over {len(nums) - 1}}\rfloor) max(1,len(nums)1max(nums)min(nums)⌋)
  2. 确定桶的数量,最后的加一保证了数组的最大值也能分到一个桶。
    桶的数量 = ⌊ max ⁡ ( n u m s ) − min ⁡ ( n u m s ) 每个桶的长度 ⌋ + 1 \lfloor{{\max(nums) - \min(nums)} \over {每个桶的长度}}\rfloor + 1 每个桶的长度max(nums)min(nums)+1
  3. 确定每个数应该对应哪个桶?
    l o c a t i o n = n u m s [ i ] − min ⁡ ( n u m s ) 每个桶的长度 location = {{nums[i] - \min(nums)} \over {每个桶的长度}} location=每个桶的长度nums[i]min(nums)

我们的做法是要将数组中的数放到一个个桶里面,不断更新更大的(后一个桶内元素的最小值 - 前一个桶内元素的最大值),最后就得到了答案。

class Solution:
    def maximumGap(self, nums: List[int]) -> int:
        if len(nums)<2: return 0
        
        max_n = max(nums)
        min_n = min(nums)
        max_gap = 0

        each_bucket_len = max(1, (max_n-min_n) // (len(nums)-1))
        buckets = [[] for _ in range((max_n-min_n) // each_bucket_len + 1)]

        # 把数字放入桶中
        for i in range(len(nums)):
            loc = (nums[i]-min_n) // each_bucket_len
            buckets[loc].append(nums[i])

        # 遍历桶更新答案
        prev_max = float('inf')
        for i in range(len(buckets)):
            if buckets[i] and prev_max != float('inf'):
                max_gap = max(max_gap, min(buckets[i])-prev_max)

            if buckets[i]:
                prev_max = max(buckets[i])

        return max_gap

时间复杂度:O(n)
空间复杂度:O(n)
例如:
nums = [1,3,4,5,6,10,11,12,17]
每个桶的长度 = (17 - 1) / (9-1) = 2
桶的个数 = (17-1)/ 2 + 1 = 9
所以我们的桶为(左闭右开):

区间[1,3)[3,5)[5,7)[7,9)[9,11)[11,13)[13,15)[15,17)[17,19)
元素13,45,61011,1217
差值3-1 = 25-4 = 110-6 = 411-10 = 117-12 = 5
答案 = max(差值) = 5

解题思路二:基数排序,其实和桶排序差不多,就是直接分为十个桶(0-9十位刚好十个桶)。然后按照个位,十位,百位…上面对应的数放入桶中进行排序。而基数排序可以在 O(N)的时间内完成整数之间的排序。

class Solution:
    def maximumGap(self, nums: List[int]) -> int:
        if len(nums)<2: return 0 # 极端情况
        
        # 基数排序
        i = 0 # 记录当前正在排拿一位,最低位为1(10**i)
        max_n = max(nums)
        j = len(str(max_n))# 记录最大值的位数
        while i < j:
            buckets = [[] for _ in range(10)] # 0-9十位刚好十个桶
            for x in nums:
                buckets[int( x / (10**i)) % 10].append(x) # 将nums中对应每位放入相应的桶中
            nums.clear()
            for x in buckets:   # 将按位排序好的桶中元素放回原数组
                for y in x:
                    nums.append(y)
            i += 1 # 进行下一位排序 个位 十位 百位...
        return max(nums[i]-nums[i-1] for i in range(1, len(nums)))

时间复杂度:O(n)
空间复杂度:O(n)

解题思路三:0


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旋转的油纸伞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值