python非递归快速排序_Python实现快速排序(非递归实现)

快速排序同样也是分治的思想,核心依然是分而治之,各个击破。

快速排序的思想是:找到一个数字x,对数组nums进行排序,使x左侧的数字都小于x,右侧的数字都大于x,然后对左侧和右侧重复同样的操作,直到所有的数字都已按序排列。

根据上面的思想,实现代码如下:

#QuickSort:used the divide and conquer idea to sort array

def QuickSort(nums:list,left:int,right:int) -> list:

'''

nums: disorderly arry

left,right: bounds of the array to be sorted

'''

if left >= right:

return

p = getIndex(nums,left,right)

QuickSort(nums,left,p-1)

QuickSort(nums,p+1,right)

return nums

def swap(nums:list,i:int,j:int):

temp = nums[i]

nums[i] = nums[j]

nums[j] = temp

def getIndex(nums:list,left:int,right:int) -> int:

'''

divide nums around x

'''

n = nums[left]

i,j = left,right

while True:

while i < right and nums[i] <= n:

i += 1

while nums[j] > n:

j -= 1

if i >= j:

break

else:

swap(nums,i,j)

nums[left] = nums[j]

nums[j] = n

return j

快排的时间复杂度受到所选择数字的影响,最坏的情况是每次找到的数字x均为最大或者最小,使得划分后一侧有n-1个数字,这样是时间复杂度为n^2,如果每次找的数字x都为中值,那么时间复杂度nlogn,这是最好的情况,快速排序的平均时间复杂度可以证明为 nlogn

对于快排的时间复杂度比较依赖于选取元的问题,我们可以使用随机取元的方法(任取left,right内的一个元素) 或者 三者取中(取nums[left],nums[mid],nums[right]三者中的中间值)

随机取元即在确定x的时候,不再是x=nums[left],而是随机的从nums[left,right]中取一个元素作为x,即令x = random.choice(range(left,right+1)),那么利用原先的代码,我们只要增加一个RandomGetIndex函数即可,实现代码如下:

def RandomGetIndex(nums:list,left:int,right:int) -> int:

temp = random.choice(range(left,right+1))

swap(nums,left,temp)

index = getIndex(nums,left,right)

return index

#我们递归函数的代码修改为如下

def QuickSort(nums:list,left:int,right:int) -> list:

'''

nums: disorderly arry

left,right: bounds of the array to be sorted

'''

if left >= right:

return

p = RandomGetIndex(nums,left,right)

QuickSort(nums,left,p-1)

QuickSort(nums,p+1,right)

return nums

三者取中的思想是希望能选取一个比较中间的值,这样可以使时间复杂度,更加趋近于nlogn,一般三者取中的策略是选择nums[left],nums[mid],nums[right],三个数里面居中的那个数字作为x,那么同样只需要增加一个ThreeGetMid函数来挑选中间值,然后调用getIndex即可:

def ThreeGetMid(nums:list,left:int,right:int) -> int:

mid = (left+right)//2

mid_num = getMidNum(nums,left,mid,right)

swap(nums,left,mid_num)

index = getIndex(nums,left,right)

return index

def getMidNum(nums:list,left:int,mid:int,right:int) -> int:

if nums[left] >= nums[mid] and nums[left] <= nums[right]:

return left

if nums[left] >= nums[mid] and nums[left] >= nums[right]:

return mid if nums[mid] >= nums[right] else right

if nums[left] <= nums[mid] and nums[left] >= nums[right]:

return left

if nums[left] <= nums[mid] and nums[left] <= nums[right]:

return mid if nums[mid] <= nums[right] else right

#我们递归函数的代码修改为如下

def QuickSort(nums:list,left:int,right:int) -> list:

'''

nums: disorderly arry

left,right: bounds of the array to be sorted

'''

if left >= right:

return

p = ThreeGetMid(nums,left,right)

QuickSort(nums,left,p-1)

QuickSort(nums,p+1,right)

return nums

在前面那篇递归算法笔记中曾经说过,递归调用是一个消耗堆栈空间的过程,那我们在进行递归调用的时候,可能就需要不得不考虑栈溢出的问题。

递归调用是一个重复自身的过程,那我们可以在程序中模拟一个堆栈,然后再使用for循环或者while循环完成其重复调用的部分,就可以将递归调用的算法,转换成非递归的实现。

那我们只需要将我们的递归函数QuickSort改写成,下面这样的代码:

#QuickSort_No_Stack

def QuickSort_No_Stack(nums:list,left:int,right:int) -> list:

temp = [left,right]

while temp:

j = temp.pop() # j = right

i = temp.pop() # i = left

index = getIndex(nums,i,j)

if i < index-1: # 压入堆栈 注意左右边界的顺序

temp.append(i)

temp.append(index-1)

if j > index+1:

temp.append(index+1)

temp.append(j)

return nums

人生不易 且行且珍惜

b5b3128abc854749b2706aead220a586.png

机器学习初学者

5dd9ba848df8478e9738ef34fa07b960.jpg

黄海广博士创建的公众号,黄海广博士个人知乎粉丝21000+,github排名全球前120名(30000+)。本公众号致力于人工智能方向的科普性文章,为初学者提供学习路线和基础资料。原创作品有:吴恩达机器学习个人笔记、吴恩达深度学习笔记等。

往期精彩回顾

那些年做的学术公益-你不是一个人在战斗

良心推荐:机器学习入门资料汇总及学习建议

吴恩达机器学习课程笔记及资源(github标星12000+,提供百度云镜像)

吴恩达深度学习笔记及视频等资源(github标星8500+,提供百度云镜像)

《统计学习方法》的python代码实现(github标星7200+)

精心整理和翻译的机器学习的相关数学资料

首发:深度学习入门宝典-《python深度学习》原文代码中文注释版及电子书

图解word2vec(原文翻译)

备注:加入本站微信群或者qq群,请回复“加群”

加入知识星球(4100+用户,ID:92416895),请回复“知识星球”

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值