分治思想--快速排序 | 优先队列:力扣215. 数组中的第K个最大元素

1、题目描述:

在这里插入图片描述

2、题解:

哈希表:力扣347. 前 K 个高频元素
方法1:暴力解法:
也就是我们进行排序(默认从小到大),然后倒序取第K个元素即可。

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        nums.sort()
        return nums[-k]

方法2:分治思想,也即是快速排序中的主要部分进行变体,我们找到第len(nums)-k个元素即可。
方法1是很容易想到的,但是用这个方法会消耗掉额外的时间,如果能求出第K个最大元素,而不管后面的元素就好了,这样能节约时间。

import random
class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        size = len(nums)
        target = size - k
        left = 0
        right = size - 1
        while True:
            index = self.__partition(nums, left, right)
            if index == target:
                return nums[index]
            elif index < target:
                # 下一轮在 [index + 1, right] 里找
                left = index + 1
            else:
                right = index - 1

    #  循环不变量:[left + 1, j] < pivot
    #  (j, i) >= pivot
    def __partition(self, nums, left, right):
        random_index = random.randint(left,right)
        nums[left],nums[random_index] = nums[random_index],nums[left]
        pivot = nums[left]
        j = left
        for i in range(left + 1, right + 1):
            if nums[i] < pivot:
                j += 1
                nums[i], nums[j] = nums[j], nums[i]

        nums[left], nums[j] = nums[j], nums[left]
        return j

当然我们可以对patition部分进行改进,我们用双指针进行partition

import random
class Solution:
    # def findKthLargest(self, nums: List[int], k: int) -> int:
    def findKthLargest(self, nums, k) :
        size = len(nums)
        target = size - k
        left,right = 0,size - 1
        while True:
            index  = self.partition(nums,left,right)
            if index == target:
                return nums[index]
            elif index < target:
                #下一轮在[index  + 1,right]里找
                left = index + 1
            else:
                right = index - 1
    #[left + 1,j] < pivot ,(j,i) >= pivot
    def partition(self,nums,left,right):
        #双指针法
        #随机切分元素,randint是包括左右区间的,也就是闭区间
        random_index = random.randint(left,right)
        nums[left],nums[random_index] = nums[random_index],nums[left] #交换第一个元素的位置和随机的位置
        pivot = nums[left]
        le = left + 1
        ri = right
        while True:
            while le <= ri and nums[le] < pivot:
                le += 1
            while le <= ri and nums[ri] > pivot:
                ri -= 1
            if le > ri:
                break
            nums[le],nums[ri] = nums[ri],nums[le]
            le += 1
            ri -= 1
        nums[left],nums[ri] = nums[ri],nums[left]
        return ri

方法3:优先队列
优先队列的思路是很朴素的。因为第 K 大元素,其实就是整个数组排序以后后半部分最小的那个元素。因此,我们可以维护一个有 K 个元素的最小堆:
1、如果当前堆不满,直接添加;
2、堆满的时候,如果新读到的数小于等于堆顶,肯定不是我们要找的元素,只有新都到的数大于堆顶的时候,才将堆顶拿出,然后放入新读到的数,进而让堆自己去调整内部结构。

import heapq
class Solution:
    # 使用容量为 k 的小顶堆
    # 元素个数小于 k 的时候,放进去就是了
    # 元素个数大于 k 的时候,小于等于堆顶元素,就扔掉,大于堆顶元素,就替换

    def findKthLargest(self, nums: List[int], k: int) -> int:
        size = len(nums)
        if k > size:
            raise Exception('程序出错')
        L = []
        for index in range(k):
            # heapq 默认就是小顶堆
            heapq.heappush(L, nums[index])

        for index in range(k, size):
            top = L[0]
            if nums[index] > top:
                # 看一看堆顶的元素,只要比堆顶元素大,就替换堆顶元素
                heapq.heapreplace(L, nums[index])
        # 最后堆顶中的元素就是堆中最小的,整个数组中的第 k 大元素
        return L[0]

扩展知识:
快速排序:
思想:数组划分,找到一个元素,然后让大于它的元素在右边,小于它的在左边;然后再在左右区间继续划分。

import random
def quick_sort(x,l=0,r = None ):
    n = len(x)
    if n == 1:
        return x
    if r == None:
        r = n - 1
    if l < r:
        index = partition(x,l,r)
        quick_sort(x,l,index - 1)
        quick_sort(x,index + 1,r)

def partition(nums,l,r):
    #双指针
    random_index = random.randint(l,r)
    # nums[l],nums[random_index] = nums[random_index],nums[l]
    pivot = nums[l]
    left,right = l + 1,r
    while True:
        while left <= r and nums[left] < pivot:
            left += 1
        while l+1 <= right and nums[right] > pivot:
            right -= 1
        if left > right:
            break
        nums[left],nums[right] = nums[right],nums[left]
        left += 1
        right -= 1
    nums[l],nums[right] = nums[right],nums[l]
    return right


if __name__ == '__main__':
    x = [7,8,5,2]
    quick_sort(x)
    print(x)

3、复杂度分析:

方法1:
时间复杂度:O(NlogN)
空间复杂度:O(1)

方法2:
时间复杂度:O(N)
空间复杂度:O(1)

方法3:
时间复杂度:O(Nlogk)
空间复杂度:O(k)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值