Python 快速排序思想及模板, 解决Leetcode215数组中的第K个最大元素快速排序

本文详细解读快速排序算法,通过选择基准值并运用分支与递归,如何将数组划分为有序部分。一步步演示了从选取基准、左右游标移动到最终排序的过程,并介绍了如何应用快速排序解决LeetCode问题如查找第K个最大元素。
摘要由CSDN通过智能技术生成

推荐一篇讲快速排序思想很明白的博文:https://blog.csdn.net/jiangtianjiao/article/details/88929408
内容如下:
基本思想
选择一个基准数,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小。然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以达到全部数据变成有序。

步骤
(1) 从数列中挑出一个基准值。
(2) 将所有比基准值小的摆放在基准前面,所有比基准值大的摆在基准的后面(相同的数可以到任一边),在这个分区退出之后,该基准就处于数列的中间位置。
(3) 递归地把基准值前面的子数列和基准值后面的子数列进行排序。

注意:基准元素/左游标/右游标都是针对单趟排序而言的, 也就是说在整个排序过程的多趟排序中,各趟排序取得的基准元素/左游标/右游标一般都是不同的。对于基准元素的选取,原则上是任意的,但是一般我们选取数组中第一个元素为基准元素(假设数组随机分布)。

排序过程示意图如下

在这里插入图片描述

选取上述数组的第一个元素6作为基准元,左游标是 i 哨兵,右游标是 j 哨兵,它们遵循的规则如下:
一、右游标向左扫描, 跨过所有大于基准元素的数组元素, 直到遇到一个小于或等于基准元素的数组元素, 在那个位置停下。二、左游标向右扫描, 跨过所有小于基准元素的数组元素, 直到遇到一个大于或等于基准元素的数组元素,在那个位置停下。

第一步:哨兵 j 先开始出动。因为此处设置的基准数是最左边的数,所以需要让哨兵 j 先开始出动,哨兵 j 逐步向左挪动,直到找到一个小于 6 的元素停下来。接下来哨兵 i 再逐步向右挪动,直到找到一个大于 6 的元素停下来。最后哨兵 i 停在了数字 7 面前,哨兵 j 停在了数字 5 面前。
在这里插入图片描述
到此第一次交换结束,接着哨兵 j 继续向左移动,它发现 4 比基准数 6 要小,那么在数字4面前停下来。哨兵 i 也接着向右移动,然后在数字 9 面前停下来,然后哨兵 i 和 哨兵 j 再次进行交换。
在这里插入图片描述
第二次交换结束,哨兵 j 继续向左移动,然后在数字 3 面前停下来;哨兵 i 继续向右移动,但是它发现和哨兵 j 相遇了。那么此时说明探测结束,将数字 3 和基准数字 6 进行交换。
在这里插入图片描述
到此第一趟探测真正结束,此时以基准点6为分界线,左边的数组元素都小于等于6,右边的数组元素都大于等于6。左边序列为3,1,2,5,4。右边序列为9,7,10,8。此时对于左边序列而言,以数字3为基准元素,重复上面的探测,探测完毕之后的序列为2,1,3,5,4。对于右边序列而言,以数字9为基准元素,也重复上面的探测,一步一步的划分最后排序完全结束。

总结来说:快速排序就是用了分支和递归的思想不断处理一个数组最后实现排序
快速排序模板如下:

def partition(nums,left, right): # 分支思想求基准值索引
    pivot = left
    i = left
    j = right
    while i<j:
        while i<j and nums[j]> nums[pivot]:
            j-=1
        while i<j and nums[i]< nums[pivot]:
            i+=1
        nums[i] , nums[j] = nums[j], nums[i]  #走到这步,说明右指针指向的值比哨兵小,左指针指向的值比哨兵大,将左右指针指向的两值互换
    nums[pivot] , nums[i] = nums[pivot], nums[i] # i和j相遇,遍历(探测)结束,将pivot指向的值与i(j)指向的值互换,此时满足nums[pivot]是分界线
    return i

def quick(nums, left, right): # 递归思想将数组排序
    if left>=right: # 无需排序
        return nums
    i = partition(nums, left, right)
    quick(nums, left, i-1)   #递归调用函数,排序pivot左边部分
    quick(nums, i+1, right)  #递归调用函数,排序 pivot右边部分
    return nums

def quick_sort(nums): # 快排
    n = len(nums)
    return quick(nums, 0, n-1)

也可以将partition()和quick()函数合二为一:

def quick_sort(nums):
    n = len(nums)
    return quick(nums, 0, n - 1)

def quick(nums,left, right):
    if left >= right:
        return nums
    pivot = left
    i = left
    j = right
    while i < j:
        while i < j and nums[j] > nums[pivot]:
            j -= 1
        while i < j and nums[i] <= nums[pivot]:
            i += 1
        nums[i], nums[j] = nums[j], nums[i]
    nums[pivot], nums[j] = nums[j], nums[pivot]
    quick(nums, left, j - 1)
    quick(nums, j + 1, right)
    return nums

Leetcode215 数组中的第K个最大元素
在这里插入图片描述
思路:根据快速排序的分治思想,每次分治我们一定可以确定一个元素的最终位置i, 并且保证nums[left…i-1]中的每个元素均小于等于nums[i],且nums[i]小于等于nums[i+1…right]的每个元素,所以只要某次划分的i为倒数第k个下标时,就已经找到了答案

def topk_split(nums, k ,left, right):
	if left<right:
		index = partition(nums, left, right) 
		if index == k:
			return
		elif index< k: #
			topk_split(nums, k, left, index-1)
		else:
			topk_split(nums, k, index+1, right)

获得前k小的数

#获得前k小的数
def topk_smalls(nums, k):
    topk_split(nums, k, 0, len(nums)-1)
    return nums[:k]

arr = [1,3,2,3,0,-19]
k = 2
print(topk_smalls(arr, k))
print(arr)

获取第k小的数

#获得第k小的数
def topk_small(nums, k):
    topk_split(nums, k, 0, len(nums)-1)
    return nums[k-1] #右边是开区间,需要-1

arr = [1,3,2,3,0,-19]
k = 3
print(topk_small(arr, k))
print(arr)

获得前k大的数

#获得前k大的数 
def topk_larges(nums, k):
    #parttion是按从小到大划分的,如果让index左边为前n-k个小的数,则index右边为前k个大的数
    topk_split(nums, len(nums)-k, 0, len(nums)-1) #把k换成len(nums)-k
    return nums[len(nums)-k:] 

arr = [1,3,-2,3,0,-19]
k = 3
print(topk_larges(arr, k))
print(arr)

获得第k大的数

#获得第k大的数 
def topk_large(nums, k):
    #parttion是按从小到大划分的,如果让index左边为前n-k个小的数,则index右边为前k个大的数
    topk_split(nums, len(nums)-k, 0, len(nums)-1) #把k换成len(nums)-k
    return nums[len(nums)-k] 

arr = [1,3,-2,3,0,-19]
k = 2
print(topk_large(arr, k))
print(arr)

只排序前k个小的数

#只排序前k个小的数
#获得前k小的数O(n),进行快排O(klogk)
def topk_sort_left(nums, k):
    topk_split(nums, k, 0, len(nums)-1) 
    topk = nums[:k]
    quicksort(topk, 0, len(topk)-1)
    return topk+nums[k:] #只排序前k个数字

arr = [0,0,1,3,4,5,0,7,6,7]
k = 4
topk_sort_left(arr, k)

只排序后k个大的数

#只排序后k个大的数
#获得前n-k小的数O(n),进行快排O(klogk)
def topk_sort_right(nums, k):
    topk_split(nums, len(nums)-k, 0, len(nums)-1) 
    topk = nums[len(nums)-k:]
    quicksort(topk, 0, len(topk)-1)
    return nums[:len(nums)-k]+topk #只排序后k个数字

arr = [0,0,1,3,4,5,0,-7,6,7]
k = 4
print(topk_sort_right(arr, k))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值