背景
解题
-图片来自geek
快排:
如果要排序数组中下标从 p 到 r 之间的一组数据,我们选择 p 到 r 之间的任意一个数据作为 pivot(分区点)。
将小于 pivot 的放到左边,将大于 pivot 的放到右边,将 pivot 放到中间
可以这样放置
但是这样就不是原地的了,所以优化成如图
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
r = len(nums)
res = Solution.inplace_quick_sort(nums,0,r-1,k)
print(nums)
return res
def inplace_quick_sort(s,p,r,k):
"""列表的就地快速排序,s为列表,a为起始索引,b为终止索引"""
if p >= r:
return s[p]
# s[r]作为基准值
pivot = s[r]
i=p
j = p
# 把除了s[r]的其他元素按照以s[r]为基准分割
# i是要插入的位置,j是要比较的元素位置
# 如果是从大到小排序,则判断j是不是比pivot大,如果大就和在前面的i交换,然后i和j都像后移动一位,也就是j要比较后面的,i前面的都是比较完的
while j < r:
if s[j] > pivot:
s[i],s[j] = s[j],s[i]
i = i+1
j = j+1
s[i],s[r] = s[r],s[i]
if k == (i+1): return s[i]
elif k > (i+1): Solution.inplace_quick_sort(s,i+1,r,k)
else: Solution.inplace_quick_sort(s,p,i-1,k)
return s[k-1]
性能分析
快排是一种原地、不稳定的排序算法
快排本身的时间复杂度也是 O(nlogn)
和归并推导相同,但是前面的代码我们进行了优化,也就是根据k来决定排序哪一个子数组
第一次分区查找,我们需要对大小为 n 的数组执行分区操作,需要遍历 n 个元素。
第二次分区查找,我们只需要对大小为 n/2 的数组执行分区操作,需要遍历 n/2 个元素。
依次类推,分区遍历元素的个数分别为、n/2、n/4、n/8、n/16.……直到区间缩小为 1。
如果我们把每次分区遍历的元素个数加起来,就是:n+n/2+n/4+n/8+…+1。
这是一个等比数列求和,最后的和等于 2n-1。所以,上述解决思路的时间复杂度就为 O(n)。