数组的第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

说明:

你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/kth-largest-element-in-an-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

题意理解

先将数组降序,再找出符合题意的第 k 个最大元素。

sort函数

直接利用sort()函数对数组进行降序排序。

JDK默认是快速排序

快速排序原理

将一组无序数据a[1]、a[2]、……a[n]按升序排列:

  1. 首先任取数据a[x]作为基准;
  2. 比较a[x]与其它数据并排序,使a[x]排在数据的第k位,并且使a[1] ~ a[k-1]中的每一个数据 < a[x],a[k+1] ~ a[n]中的每一个数据 > a[x];
  3. 然后采用分治的策略分别对a[1] ~ a[k-1] 和 a[k+1] ~ a[n]两组数据进行快速排序。

冒泡排序

目前最熟悉最先想到的排序方法,两层循环遍历,通过比较相邻元素交换大小值位置。

  • 时间复杂度 O(N2)
    N 表示数组长度,最坏情况是原数组升序排列。

堆排序

堆积是一个近似完全二叉树的结构,并满足性质:子结点的键值或索引总是小于(或者大于)它的父节点。

算法描述

  1. 将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
  2. 将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];
  3. 由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。

不断重复此过程直到有序区的元素个数为 n-1,则整个排序过程完成。

解题思路

  1. 取nums前K个元素建立大小为K的最小堆;
  2. 剩余k+1到N个元素依次和堆顶比较,比堆顶大则替换当前堆顶,并维护最小堆;
  3. 最终最小堆里是前K大的元素,堆顶为前K大的元素中最小的元素,即Kth大的元素

提交代码

sort函数

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

在这里插入图片描述

冒泡排序

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        n = len(nums)
        for i in range(n-1):
            for j in range(n-1-i):
                if nums[j] < nums[j+1]:
                    temp = nums[j]
                    nums[j] = nums[j+1]
                    nums[j+1] = temp               
        return nums[k-1]

超出时间限制
在这里插入图片描述
输入数组元素数量很多时耗时长。

堆排序

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        
        
        # 替换nums[i]后维护最小堆:自顶向下调整新元素位置,直至该值满足(parent value < son value)
        def shift(i,k):
            flag=0
            while (i*2+1)<k and flag==0 :
                t=i
                if nums[i]>nums[2*i+1]:            
                    t=2*i+1
                if (i*2+2)<k and nums[t]>nums[2*i+2]  :            
                    t=2*i+2
                if t==i:
                    flag=1
                else :
                    nums[i],nums[t]=nums[t],nums[i]
                    i=t  
        
        #O(k):建立大小为K的最小堆, k/2-1是最后一个非叶节点,因为shift是向下调整,所以倒序从最下面出发,不然(4 32 1)->(2 34 1)->(2 14 3)->(2 14 3) 结果不对
        for i in range(k/2,-1,-1):
            shift(i,k)

        #O((N-k)logK),剩余元素依次比较替换
        for i in range(k,len(nums)):
            if nums[0]<nums[i]:
                nums[0]=nums[i]
                shift(0,k)
        return nums[0]
        
#https://leetcode-cn.com/problems/kth-largest-element-in-an-array/solution/quan-shou-xie-20xing-kge-yuan-su-de-zui-xiao-dui-d/

总结

学习新的排序思想——堆排序,适合解决 top k 的问题。

选择排序算法准则: 每种排序算法都各有优缺点。因此,在实用时需根据不同情况适当选用,甚至可以将多种方法结合起来使用。 选择排序算法的依据 影响排序的因素有很多,平均时间复杂度低的算法并不一定就是最优的。相反,有时平均时间复杂度高的算法可能更适合某些特殊情况。同时,选择算法时还得考虑它的可读性,以利于软件的维护。一般而言,需要考虑的因素有以下四点: 1.待排序的记录数目n的大小; 2.记录本身数据量的大小,也就是记录中除关键字外的其他信息量的大小; 3.关键字的结构及其分布情况; 4.对排序稳定性的要求。 设待排序元素的个数为n. 1)当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序序。 快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短; 堆排序 : 如果内存空间允许且要求稳定性的, 归并排序:它有一定数量的数据移动,所以我们可能过与插入排序组合,先获得一定长度的序列,然后再合并,在效率上将有所提高。 2) 当n较大,内存空间允许,且要求稳定性 =》归并排序 3)当n较小,可采用直接插入或直接选择排序。 直接插入排序:当元素分布有序,直接插入排序将大大减少比较次数和移动记录的次数。 直接选择排序 :元素分布有序,如果不要求稳定性,选择直接选择排序 5)一般不使用或不直接使用传统的冒泡排序。 6)基数排序 它是一种稳定的排序算法,但有一定的局限性:   1、关键字可分解。   2、记录的关键字位数较少,如果密集更好   3、如果是数字时,最好是无符号的,否则将增加相应的映射复杂度,可先将其正负分开排序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值