必刷?215. 数组中的第K个最大元素之:快排+归排+快速选择

链接:https://leetcode.cn/problems/kth-largest-element-in-an-array/
这道题可以用来练习快排、归排、快速选择三种算法,性价比非常高,建议隔天一刷。

1.题目描述

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

示例 1:

输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:

输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4

2.快排解法

把快排模板理解了并背住,直接就上手写了。但是快排模板非常多,这时候一个简单好记的就尤为重要,这里给出一个较为简洁的版本。

class Solution:
    """
    快排解法: 写法二(更加简介) 时间 O(nlogn) 空间O(1)
    """

    def findKthLargest(self, nums: List[int], k: int) -> int:
        if not nums:
            return -1

        self.quick_sort(nums, 0, len(nums) - 1)
        return nums[-k]

    def quick_sort(self, nums, begin, end):
        if begin >= end:
            return

        pivot = nums[(begin + end) >> 1]
        left, right = begin, end
        while left <= right:
            while left <= right and nums[right] > pivot:
                right -= 1
            while left <= right and nums[left] < pivot:
                left += 1
            if left <= right:
                nums[left], nums[right] = nums[right], nums[left]
                left += 1
                right -= 1
        self.quick_sort(nums, begin, right)
        self.quick_sort(nums, left, end)

3.归并排序

归并排序也是面试热门考点,虽然没快排频率那么高,但是多练练也没坏处。归并排序还要O(n)的额外空间消耗,所以实际用得比较少,这里采用思维复杂度比较低(较简单)的归并方法:采用原数组大小的额外空间。其实也可以不用开这么大的数组,可以另外试试一道合并有序数组的题:88. 合并两个有序数组

class Solution:
    """
    归并排序: 时间 O(nlogn) 空间O(n)
    """

    def findKthLargest(self, nums: List[int], k: int) -> int:
        if not nums:
            return -1
        temp = [0] * len(nums)
        self.merge_sort(nums, 0, len(nums) - 1, temp)

        return nums[-k]

    def merge_sort(self, nums, begin, end, temp):
        if begin >= end:
            return
        mid = (begin + end) >> 1
        self.merge_sort(nums, begin, mid, temp)
        self.merge_sort(nums, mid + 1, end, temp)
        # merge
        self.merge(nums, begin, end, temp)

    def merge(self, nums, begin, end, temp):
        mid = (begin + end) >> 1
        left, right = begin, mid + 1
        index = begin
        while left <= mid and right <= end:
            if nums[left] <= nums[right]:
                temp[index] = nums[left]
                left += 1
            if nums[left] > nums[right]:
                temp[index] = nums[right]
                right += 1
            index += 1

        while left <= mid:
            temp[index] = nums[left]
            left += 1
            index += 1
        while right <= end:
            temp[index] = nums[right]
            right += 1
            index += 1

        for i in range(begin, end + 1):
            nums[i] = temp[i]

3.快速选择

快速选择算法 时间O(n),非常强大,其实也是使用了快排的思想,只不过不再做全排序,只通过缩小区间找第k大的数。下面给出了代码和时间复杂度证明。

class Solution:
    """
    Quick Select: 时间O(n)
    T(n) = O(n) + T(n/2)
         = O(n) + O(n/2) + T(n/4)
         = O(n) + O(n/2) + O(n/4) + T(n/8)
         = O(n) + O(n/2) + O(n/4) + ... + O(2) + T(1)
         = O(n) + O(n/2) + ... + O(2) + O(1)
    limT(n)_{n->inf} = (a0 - an*q) / (1-q)
                     = (n - 1/2) / (1/2)
                     = 2n - 1
                     = O(n)
    """
    def findKthLargest(self, nums: List[int], k: int) -> int:
        if not nums:
            return -1
        return self.quick_select(nums, 0, len(nums) - 1, k)

    def quick_select(self, nums, begin, end, k):
        if begin == end:
            return nums[begin]

        index = (begin + end) >> 1
        nums[begin], nums[index] = nums[index], nums[begin]
        pivot = nums[begin]
        left, right = begin, end
        while left < right:
            while left < right and nums[right] < pivot:
                right -= 1
            if left < right:
                nums[left] = nums[right]
                left += 1
            while left < right and nums[left] > pivot:
                left += 1
            if left < right:
                nums[right] = nums[left]
                right -= 1
        nums[left] = pivot

        if (begin + k - 1) < left:
            return self.quick_select(nums, begin, left - 1, k)
        if (begin + k - 1) > left:
            return self.quick_select(nums, left + 1, end, k - (left + 1 - begin))
        return pivot
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值