【AcWing-Python-786】第k个数/快速选择算法

文章介绍了快速排序和快速选择两种算法,强调了它们的时间复杂度和分治策略。在快速排序中,通过找到分界点将数组划分为两部分,递归处理直到找到目标。而快选只需处理一侧区间。提供了Python代码示例来实现快速排序,用于找到数组中的第k大数。
摘要由CSDN通过智能技术生成
题目: https://www.acwing.com/problem/content/788/
对应视频讲解: https://www.acwing.com/video/228/

题目描述


回顾快排

【AcWing-Python-785】快速排序 - CSDN博客

(一)步骤

  1. 找到分界点x:可以是区间最左端点、区间最右端点、或区间中点

  1. 将整个区间划分为两段,使得左边所有数Left ≤ x,右边所有数Right ≥ x【注意分界点位置的值不一定为x】

  1. 递归排序Left,递归排序Right

(二)时间复杂度

排序的时间复杂度是O(n * logn),快速排序的时间复杂度是O(n)

(三)与快选的联系

经过步骤1和2后,整个区间里,在分界点左边的数都≤x(假设长度为SL),右边的数都≥x(假设长度为SR)。现在想找到第k大的数

  • 若 k ≤ SL,则整个区间第k小的数一定在左边,此时只需递归处理左边即可

  • 若 k >SL,则整个区间第k小的数一定在右边,此时只需递归处理右边即可

快排需要递归左右两边,而快选只需要递归一边即可

快选的时间复杂度为O(n)

  • 先对整个区间划分两部分,使左边都≤x,右边都≥x,这步需要扫描整个区间n,故为O(n)

  • 根据第k小的数的k,与左边区间的长度SL的大小关系,判断是递归处理左边还是右边,只需扫描一半的区间即可,故为O(n/2)

  • 以此类推,n + n/2 + n/4 + n/8 + ... = n (1 + 1/2 + 1/4 + 1/8 + ...) ≤ 2n,故为O(2n),因为常数不影响,故还是O(n)


解题思路及代码

(一)思路

首先明确,第k大的数X在有序数组中的位置为k-1(i ∈ [0, n-1])

采用快排分治思想,每次可只处理X所在的那一半区间,所以在进行快排找分界点时,找到此次递归的数组分界点j,将此次递归区间 [l, r] 分为 [l, j] 和 [j+1, r] 左右两个区间,并确定左右区间中的数,这些数在有序数组中也处于该区间,这样就可以根据j来确定k-1在左区间还是在右区间

  • 如果 k - 1 <= j,说明k - 1在左区间,则递归处理左区间,舍去(不处理)右区间

  • 反之如果k - 1 > j,说明k - 1在右区间,则递归处理右区间,舍去(不处理)左区间

不断递归,区间长度为1时,则找到了在有序数组中下标为k-1的数X

(二)代码

n, k = map(int, input().split())
nums = list(map(int, input().split()))

def quick_sort(arr, left, right):
    
    if left >= right:
        return
    
    i, j, x = left-1, right+1, arr[(left+right)//2]
    
    while i < j:
        i += 1
        j -= 1
        
        while arr[i]<x:
            i += 1
        while arr[j]>x:
            j -= 1
        
        if i<j:
            arr[i], arr[j] = arr[j], arr[i]   
    
    # 此时j左边都是≤x的数,j右边都是≥x的数
    # 要找到排序后第k小的数,也即下标为 k-1 的数
    if k-1 <= j:
        quick_sort(arr, left, j)
        
    if k-1 > j:
        quick_sort(arr, j+1, right)

quick_sort(nums, 0, n-1)
print(nums[k-1])
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Cheer-ego

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

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

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

打赏作者

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

抵扣说明:

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

余额充值