快排问题是面试常考的基础题,leetcode 排序数组 就是一道专门练手数组排序的题目,快排的思路就在于随机选择一个数作为pivot,使得pivot左边的数都小于nums[pivot], 右边的数都大于等于nums[pivot], 代码的核心就在选择好pivot数之后,如何写代码使得左边的数都小于nums[pivot], 右边的数都大于等于nums[pivot]
我一开始代码如下:
def quicksort(self, nums, start, end):
if start >= end:
return
if end - start + 1 == 2:
if nums[end] < nums[start]:
nums[start], nums[end] = nums[end], nums[start]
return
index = random.randint(start, end)
nums[index], nums[end] = nums[end], nums[index]
i, j = start, end-1
while i < j:
while nums[i] < nums[end]:
i += 1
nums[i], nums[j] = nums[j], nums[i]
j -= 1
nums[i], nums[end] = nums[end], nums[i]
self.quicksort(nums, start, i - 1)
self.quicksort(nums, i + 1, end)
提交自己的程序在一些case上面报错了,如这个case,[-2, -1, -1, 0, -1, 0], 并且这个case的错误是随机出现,因为pivot选择是随机的。
经过调试发现发现我的错误在于,当index 为5, i=3 的时候,这时数组已经是[-2, -1, -1, -1, 0, 0], 按理已经排好序,但是接下来的一步nums[i], nums[end] = nums[end], nums[i]
就导致变成了[-2, -1, -1, 0, 0, -1],导致程序出错,分析研究在于目前程序能够保证 s t a r t → i − 1 start \to i-1 start→i−1 都是 < n u m s [ e n d ]
def quicksort(self, nums, start, end):
if start >= end:
return
if end - start + 1 == 2:
if nums[end] < nums[start]:
nums[start], nums[end] = nums[end], nums[start]
return
index = random.randint(start, end)
nums[index], nums[end] = nums[end], nums[index]
i, j = start, end-1
while i < j:
while nums[i] < nums[end]:
i += 1
nums[i], nums[j] = nums[j], nums[i]
j -= 1
if nums[end] <= nums[i]:
nums[i], nums[end] = nums[end], nums[i]
self.quicksort(nums, start, i - 1)
self.quicksort(nums, i + 1, end)
else:
nums[i+1], nums[end] = nums[end], nums[i+1]
self.quicksort(nums, start, i)
self.quicksort(nums, i+2, end)
虽然程序通过了平台的测试,但是自己的代码逻辑实在不elegant,和印象中快排不一样,为此自己对比的了leetcode官方题解,具体如下:
def randomized_partition(self, nums, l, r):
pivot = random.randint(l, r)
nums[pivot], nums[r] = nums[r], nums[pivot]
i = l - 1
for j in range(l, r):
if nums[j] < nums[r]:
i += 1
nums[j], nums[i] = nums[i], nums[j]
i += 1
nums[i], nums[r] = nums[r], nums[i]
return i
和自己的题解一对比,官网的题解非常的清晰,妙在定义了一个变量 i i i来记录当前所有小于 n u m s [ r ] nums[r] nums[r]的最大索引,并且要在 l − 1 l-1 l−1开始,因为你不一直到 n u m s [ l ] 与 n u m s [ r ] nums[l] 与nums[r] nums[l]与nums[r]的大小关系,最后不要忘了 i + = 1 i+=1 i+=1 作为新的和 n u m s [ r ] nums[r] nums[r] 交换的索引。
快排是一道非常经典的排序题,但是面试时拿起笔写的时候却一时没有思路,其核心思路是将待排序序列分成前后两个序列,保证前的序列的数都大于后面的序列,在分别递归地对前后两个序列进行快排操作,从而实现最终的排序。在寻找pivot的时候,先随机一个index,然后设置一个变量,记录当前小于随机出来的数的最大索引,每找到一个新的小于当前随机pivot的数,该变量就加1,最后介绍循环后该变量还要继续加1,并交换两个索引指向的数,从而实现将待排序序列分成前后两个序列。