Python实现查找第K小元素(上)--快排思想

今天抽时间实现的是查找第 K K K小元素算法。

问题描述:给定线性序集中 n n n个元素和一个整数 k ( 1 < = k < = n ) k (1<=k<=n) k(1<=k<=n),要求找出这 n n n个元素中第 k k k小的元素。

对于这个问题,一般可以直接想到的方法就是,先对这 n n n个数字进行排序,然后直接选取第k个元素,排序算法可以用我们之前实现过的归并排序或者快速排序,这样实现的算法时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn)

我们最近一直在学习分治思想,所以就考虑能否将分治的思想应用到第 k k k个元素的选择问题中,将时间复杂度降下来呢?

我们可以对数组进行类似快排那样将数据分成两段,得到排序后随机元 i i i的位置 p p p,如果 k k k等于 p + 1 p+1 p+1,则说明 i i i就是第 k k k小的元素,如果 k k k小于 p + 1 p+1 p+1,说明第 k k k小的元素在随机元 i i i的左侧,否则,第 k k k小的元素在随机元 i i i的右侧。

对选中的一段重复前面类似快排的操作,直到找到全局第 k k k个元素。具体内容结合下面的代码来看:

def Select_K(nums:list,left:int,right:int,k:int) -> int:
    if left > right:
        return 
    if k > len(nums) or k <= 0:
        return 'k is out of range'
    
    p = getIndex(nums,left,right)
    
    if k == p+1:
        return nums[p]
    if k < p+1:
        return Select_K(nums,left,p-1,k)
    else:
        return Select_K(nums,p+1,right,k)
#Select_K函数中使用了尾递归,可以消除栈溢出的情况,具体什么是尾递归,以后会有开一个专题来说
    
def swap(nums:list,i:int,j:int):
    temp = nums[i]
    nums[i] = nums[j]
    nums[j] = temp
    
def getIndex(nums:list,left:int,right:int) -> int:
    n = nums[left]
    i,j = left,right
    
    while True:
        while i < right and nums[i] <= n:
            i += 1 
        while nums[j] > n:
            j -= 1
        if i >= j:
            break
        else:
            swap(nums,i,j)
            
    nums[left] = nums[j]
    nums[j] = n
    return j
    

通过上面的方法,可以得到第 k k k小的值,但是在算法的复杂度上来看只是快排的一半,时间复杂度并没有降多少,但是经过大佬证明:当我们使用随机取元(RandomGetIndex)方法获得随机元 i i i的时候,平均的搜索时间可以降到线性时间内,也就是平均时间复杂度为 O ( n ) O(n) O(n)

那么,能否找到一种方法,使得最坏情况下的搜索也能在线性时间内完成呢?答案是可以的,但是我们将在下篇博客中进行讲解实现。

路漫漫其修远兮,吾将上下而求索。

你可能感兴趣的:(算法设计与分析,Python,查找第K小元素,查找第K个元素,查找和排序)