215. 数组中的第K个最大元素

思路1:快排
考虑先使用快排实现,练习快排。
时间复杂度O(N):如果我们把每次分区遍历的元素个数加起来,就是:n+n/2+n/4+n/8+…+1。这是一个等比数列求和,最后的和等于 2n-1。所以,时间复杂度就为 O(n)。
因为一次快排可以实现一个数归位,比较index和n-K的大小
如果index==n-K就找到了
空间复杂度 : O(1)

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        def getindex(low,high): # 返回依次排序,首元素就位后的索引
            key = nums[low]
            while low<high:
                while low<high and nums[high]>=key:
                    high-=1
                nums[low]=nums[high]
                while low<high and nums[low]<=key:
                    low+=1
                nums[high] = nums[low]
            # 跳出循环时low和high相等,此时的low或high就是key的正确索引位置
            nums[low] = key
            return low
            
        n = len(nums)
        low,high = 0,n-1
        index = getindex(low,high)
        while index!=n-k:
            if n-k<index:
                high = index-1
            else:
                low = index+1
            index = getindex(low,high)
        return nums[index]

在这里插入图片描述
优化:虽然时间复杂度不高,但是运行也太慢了。对比了官方解答,发现快很多。关键点在于每次不以索引0作为pivot,而是n-k。这样能够避免极端测试用例,通常能够更快的找到答案。

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        def getindex(low, high,pivot):  # 修改
            key = nums[pivot]			 # 修改
            nums[pivot] = nums[low]       # 修改 需要把首元素先复制出来,否则会丢失
            while low < high:
                while low < high and nums[high] >= key:
                    high -= 1
                nums[low] = nums[high]
                while low < high and nums[low] <= key:
                    low += 1
                nums[high] = nums[low]
            # 跳出循环时low和high相等,此时的low或high就是key的正确索引位置
            nums[low] = key
            return low

        n = len(nums)
        low, high = 0, n - 1
        index = getindex(low, high,0)   # 修改
        while index != n - k:
            if n - k < index:
                high = index - 1
            else:
                low = index + 1
            index = getindex(low, high,n-k)   # 修改
        return nums[index]

在这里插入图片描述
思路2
库函数 heapq.nlargest(k,nums)
时间复杂度O(nlogk)

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        return heapq.nlargest(k,nums)[-1] # 堆的前k个数,逆序排列,取末尾的最小值

在这里插入图片描述
思路3:手写快排
参考 powcai-排序

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        def adjust_maxheap(idx,maxlen):
            left = 2*idx+1      # 左孩子
            right = 2*idx+2     # 右孩子
            new_max = idx       # 新的最大值的索引初始值
            if left<maxlen and nums[left]>nums[new_max]:
                new_max = left
            if right <maxlen and nums[right]>nums[new_max]:
                new_max = right
            if new_max !=idx:       # 如果新的最大值不是旧的最大值,即需要进行调整
                nums[new_max],nums[idx] = nums[idx],nums[new_max]
                adjust_maxheap(new_max,maxlen)      # 那么需要把新的做大值的树进行继续调整

        # 建堆
        n = len(nums)
        for i in range(n//2-1,-1,-1): # 起点为倒数第一个非叶子节点,终点是第一个节点
            adjust_maxheap(i,n)
        # print(nums)

        # 取数调整堆
        for j in range(1,k): # 循环k-1次调整,此时第k大的值在根位置
            nums[0],nums[-j] = nums[-j],nums[0]
            adjust_maxheap(0,n-j)
        return nums[0]

在这里插入图片描述

可以使用堆排序来解决该问题,其堆排序是一种时间复杂度为O(n log n)的排序算法,可以在这里用来找到第k个最大元素。 堆排序的基本思想是将待排序序列构造成一个堆,然后依次将堆顶元素和堆底元素交换,每次交换后重新调整堆,直到所有元素都排好序为止。 具体实现步骤如下: 1. 定义一个小根堆 `minHeap`,将 `nums` 数组的前 `k` 个元素加入到小根堆。 2. 从第 `k + 1` 个元素开始遍历 `nums` 数组,如果当前元素比小根堆堆顶元素大,就将堆顶元素弹出,并将当前元素加入到小根堆。 3. 遍历结束后,小根堆的堆顶元素就是第k个最大元素。 完整的代码如下: ```kotlin fun findKthLargest(nums: IntArray, k: Int): Int { // 将前k个元素加入小根堆 val minHeap = PriorityQueue<Int>() for (i in 0 until k) { minHeap.offer(nums[i]) } // 遍历数组,将比堆顶元素大的元素加入堆,弹出堆顶元素 for (i in k until nums.size) { if (nums[i] > minHeap.peek()) { minHeap.poll() minHeap.offer(nums[i]) } } // 堆顶元素就是第k个最大元素 return minHeap.peek() } ``` 在上述代码,我们使用了 `java.util.PriorityQueue` 类来实现小根堆,其 `minHeap.offer()` 方法用于加入元素,`minHeap.peek()` 方法用于获取堆顶元素,`minHeap.poll()` 方法用于弹出堆顶元素。由于小根堆只保留前k个最大元素,因此堆元素个数不会超过k,时间复杂度为O(n log k)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值