【Python】优雅的快速选择 - 快速排序 - 随机快速排序

快速选择(递归实现版)

这里给出以 “leetcode215. 数组中的第K个最大元素”为例的代码。

class Solution:
    def findKthLargest(self, nums, k):
        self.nums = nums
        n = len(nums)
        return self.quickSelect(0,n-1,n-k)
    

    def quickSelect(self,l,r,k): # 手撸快速选择
        if(l == r): return self.nums[k]
        
        mid_num = self.nums[l]
        i = l-1
        j = r+1
        while(i < j):
            i += 1
            while(self.nums[i] < mid_num): i += 1
            j -= 1
            while(self.nums[j] > mid_num): j -= 1
            if(i < j): self.nums[i],self.nums[j] = self.nums[j],self.nums[i]

        if(k <= j): return self.quickSelect(l,j,k)
        return self.quickSelect(j+1,r,k)

代码的实现参照了官解,非常优雅。

代码的关键是,"while(i < j):"这一步里面的循环。可以证明,经过这个循环,最后出来的j,其位置一定在区间[l,r-1]上,同时[l,j]元素的值均等小于等于[j+1,r]。因此,每次递归,都会导致输入的数组长度减少,递归可以退出。由于寻找的是下标为k的排序的元素,因此每次分割,一定可以确定有一边是不必再排序的,因此进入下一递归的区间,就选择[l,j]或[j+1,r],直到某个递归中l=r,这时代表只有一个元素,没必要排序,直接返回此时列表的下标k的元素即可得到。

注意代码实现中的,选择第一个元素作为基准值的设计很精妙,可以仔细品鉴。

快速选择(迭代实现)

class Solution:
    def findKthLargest(self, nums, k):
        self.nums = nums
        n = len(nums)
        return self.quickSelectIter(0,n-1,n-k) 

    def quickSelectIter(self,l,r,k): # 手撸快速选择
        while(l != r):
            mid_num = self.nums[l]
            i = l-1
            j = r+1
            while(i < j):
                i += 1
                while(self.nums[i] < mid_num): i += 1
                j -= 1
                while(self.nums[j] > mid_num): j -= 1
                if(i < j): self.nums[i],self.nums[j] = self.nums[j],self.nums[i]
            if(k <= j):
                l,r = l,j
            else:
                l,r = j+1,r
        return self.nums[k]

快速排序

上面的快速选择,每次划分成两个区间,然后抛弃另一个区间,继续划分留下的区间。
对上面的代码稍加改进,对每个划分出的区间继续划分,即可得到完全排序的数组。

这里以"leetcode 912. 排序数组"给出代码

class Solution:
    def sortArray(self, nums: List[int]) -> List[int]:
        self.nums = nums
        self.quickSort(0,len(nums)-1)
        return nums
    
    def quickSort(self,l,r):
        if(l == r): return
        
        mid_num = self.nums[l]
        i = l-1
        j = r+1
        while(i < j):
            i += 1
            while(self.nums[i] < mid_num): i += 1
            j -= 1
            while(self.nums[j] > mid_num): j -= 1
            if(i < j): self.nums[i],self.nums[j] = self.nums[j],self.nums[i]

        self.quickSort(l,j)
        self.quickSort(j+1,r)

随机快速排序

上面的快速排序,取第一个元素为基准值,会导致在极端情况下,时间开销大。比如数组已经是有序的情况。

这里做出改进,随机选取数组中的一个元素作为基准值。但是,前面快速选择的方法中实现的划分是精心设计的,其要求基准值是第一个元素。
因此,在随机选择基准值并划分的实现上,只需先随机选取一个下标,并将下标值和第一个值互换,其它操作和之前一样即可。

class Solution:
    def sortArray(self, nums: List[int]) -> List[int]:
        self.nums = nums
        self.quickSortRandom(0,len(nums)-1)
        return nums

    # 随机快速排序
    def quickSortRandom(self,l,r):
        if(l == r): return
        
        random_index = random.randint(l, r)
        self.nums[l],self.nums[random_index] = self.nums[random_index],self.nums[l]
        mid_num = self.nums[l]
        i = l-1
        j = r+1
        while(i < j):
            i += 1
            while(self.nums[i] < mid_num): i += 1
            j -= 1
            while(self.nums[j] > mid_num): j -= 1
            if(i < j): self.nums[i],self.nums[j] = self.nums[j],self.nums[i]

        self.quickSortRandom(l,j)
        self.quickSortRandom(j+1,r)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值