快速选择(递归实现版)
这里给出以 “leetcode215. 数组中的第K个最大元素”为例的代码。
class Solution:def findKthLargest(self, nums, k):self.nums = numsn = 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-1j = r+1while(i < j):i += 1while(self.nums[i] < mid_num): i += 1j -= 1while(self.nums[j] > mid_num): j -= 1if(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 = numsn = 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-1j = r+1while(i < j):i += 1while(self.nums[i] < mid_num): i += 1j -= 1while(self.nums[j] > mid_num): j -= 1if(i < j): self.nums[i],self.nums[j] = self.nums[j],self.nums[i]if(k <= j):l,r = l,jelse:l,r = j+1,rreturn self.nums[k]
快速排序
上面的快速选择,每次划分成两个区间,然后抛弃另一个区间,继续划分留下的区间。
对上面的代码稍加改进,对每个划分出的区间继续划分,即可得到完全排序的数组。
这里以"leetcode 912. 排序数组"给出代码
class Solution:def sortArray(self, nums: List[int]) -> List[int]:self.nums = numsself.quickSort(0,len(nums)-1)return numsdef quickSort(self,l,r):if(l == r): returnmid_num = self.nums[l]i = l-1j = r+1while(i < j):i += 1while(self.nums[i] < mid_num): i += 1j -= 1while(self.nums[j] > mid_num): j -= 1if(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 = numsself.quickSortRandom(0,len(nums)-1)return nums# 随机快速排序def quickSortRandom(self,l,r):if(l == r): returnrandom_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-1j = r+1while(i < j):i += 1while(self.nums[i] < mid_num): i += 1j -= 1while(self.nums[j] > mid_num): j -= 1if(i < j): self.nums[i],self.nums[j] = self.nums[j],self.nums[i]self.quickSortRandom(l,j)self.quickSortRandom(j+1,r)