目录
NB三人组
快速排序
快速排序原理:我们设首元素为我们要归位(partition)的元素,partition完(这里指升序,降序反之),左边(left,mid-1)为比mid(首元素归位后的下标即索引)小的数(mid左边的数无序的,但都比mid小),右边(mid+1,right)为比mid大的数(mid右边的数是无序,但都比mid大);再将左边当成一个整体,进行归位,归位完又被分为左部分和右部分… 利用递归,可将列表不断进行归位,实现快速排序。
时间复杂度为O(nlogn),空间复杂度为O(logn),不稳定,代码较复杂
import sys
sys.setrecursionlimit(10000) # 更改递归深度,默认(999)
def partition(li, left, right): # 归位函数
temp = li[left] # 我们将首元素作为归位目标,实现左边比temp小,右边比temp大。降序反之。
while left < right:
while left < right and li[right] >= temp: # 将后面比temp小的移到前面
right -= 1
li[left] = li[right]
while left < right and li[left] <= temp: # 将前面比temp大的移到后面
left += 1
li[right] = li[left]
li[left] = temp # while循环终止,此时left=right,temp归位到left(right),满足左边比temp小,右边比temp大。
return left
def quick_sort(li, left, right): # 利用递归的排序函数
if left < right: # 进行递归要有条件终止
print(li)
mid = partition(li, left, right)
# 进行递归
quick_sort(li,left,mid-1) # 将左边递归归位,实现大小排序
quick_sort(li,mid+1,right) # 将右边边递归归位,实现大小排序
return li
b = [4,5,2,3,1]
quick_sort(b, 0, len(b)-1)
print(b)
"""
将首元素4归位得第2步,[1,3,2]为左边,[5]为右边(此时left=right,归位结束)
[1,3,2] 将首元素1归位得:[]空列表为左边(right<left,归位结束),[3,2]为右边
[3,2] 将首元素3归位为得:[2]为左边(此时left=right,归位结束),[]空列表为右边(right<left,归位结束)
1、 [4, 5, 2, 3, 1]
2、 [1, 3, 2, 4, 5]
3、 [1, 3, 2, 4, 5]
4、 [1, 2, 3, 4, 5]
"""
堆排序
首先我说明一下 写法一 的堆排序的实现过程,方便小伙伴 们理解:我们先了解一下 向下调整函数 sift 的作用:列表li,把他看堆来理解,他是一个 根节点low不是最大,其余元素都满足大根堆型的 大根堆型的完全二叉树(这种说法是错误的,为了方便理解叫大根堆),sift向下调整就是将根节点low 放到他本来该按照大小的堆排序位置,即实现 正确的大根堆型完全二叉树,然后通过循环,将列表(堆)li的各个部分实现大根堆,从小的部分到大的部分,依次实现,最后完成整体实现 大根堆型完全二叉树。总结一下:sift函数将列表li实现为大根堆型完全二叉树;为了满足能省则省,原地排序原则,可将大根堆的根根节点low与最后一个元素调换位置,再进行深度减去1的循环,将堆里面最大的元素放的堆顶,再调换位置,深度减小…即可实现升序的堆排序
堆排序原理:原地排序写法:将列表li建立成大根堆完全二叉树(这是指升序,降序则小根堆),通过循环,将根节点low与最后一个元素n-1调换位置,深度减少,通过sift将最大元素归位到根节点low,再与最后一个元素n-2调换位置,深度减少…最大的元素不断的归位到列表后面,实现原地排序
时间复杂度为O(nlogn),空间复杂度为O(1),不稳定,速度相比快排,归并较慢,十分难写
写法一:原地排序
def sift(li, low, high):
"""
向下调整函数:列表li是满足 除了根节点low不是最大的 完全二叉树(大根堆型),注:实质上 li的low不是最大的 不可以叫大根堆,这里为了便于理解。
:param li: 需要调整为(大根堆)完全二叉树的堆(此时只有 堆li 的根节点 不满足最大,即需将low向下调整实现大根堆)
:param low: 堆的根节点位置
:param high: 堆的最后一个元素的下标
:return: 根节点low向下调整所实现的 大根堆型的完全二叉树
"""
i = low
j = 2*i + 1 # 开始时 j 为一个左孩子
temp = li[low] # 将根节点值存起来,方便将low实现向下调整
while j <= high:
if j+1 <= high and li[j] < li[j+1]: # 如果右孩子存在(即在high深度以内)且比左孩子大,那么将左孩子指向右孩子,实现将大的元素移想对顶
j = j + 1 # 不可将左孩子和右孩子的值互换,因为右孩子的子孩子有可能比左孩子大,这样会破坏大根堆原则
if li[j] > temp:
li[i] = li[j]
i = j
j = 2*i +1 # 将 i(low) 往下一层深度
else: # 存在比low小的父亲节点,那么将low存放
li[i] = temp
break
else: # j已经超出最大堆深度high,while结束,将low存放在最深深度,因为此时low比父亲节点都小了
li[i] = temp
def heap_sort(li):
n = len(li)
# 建立 大根堆型的完全二叉树
for i in range((n-1-1)//2, -1, -1): # i表示调整的时候,调整的部分 的堆的根节点下标
sift(li, i, n-1) # n-1 可以作为 各个堆的深度
# 通过循环,减小深度,将堆的最大值放到堆深度的外面(仍然在li的范围)实现原地排序,能省则省原则
for i in range(n-1, -1, -1):
li[0], li[i] = li[i], li[0]
sift(li, 0, i-1) # i-1为新的深度high,将新的堆再次向下调整,实现将最大的元素放到堆顶
import random
b = list(range(1, 10))
random.shuffle(b)
print(b)
heap_sort(b)
print(b)
写法二:利用内置函数heapq
import heapq
import random
b = list(range(1, 15))
random.shuffle(b)
print(b)
heapq.heapify(b) # heapify 建堆
b_new = []
for i in range(len(b)):
b_new.append(heapq.heappop(b)) # heappop 会每次抛出堆里面的最小值
print(b_new)
"""
输出结果
[14, 3, 12, 11, 1, 7, 6, 10, 13, 9, 8, 4, 5, 2]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
"""
归并排序
归并排序原理:我们假设mid为列表的某一索引值,mid左边的元素都是有序的,mid右边的元素都是有序的,那我们只需将左边和右边的元素依次比较大小(我们这里左升序排序,降序反之),循环将小的存入新的列表中,循环结束时,新列表就是我们所预期的升序排序。上述的前提是mid左右两边都是有序的,我们可以通过递归实现mid左右两边的有序。
时间复杂度为O(nlogn),空间复杂度为O(n),稳定,代码较复杂
def merge(li, low, mid, high): # mid左边为有序元素,mid右边为有序元素
i = low
j = mid + 1
temp = [] # 创建一个新列表
while i <= mid and j <= high: # i,j不能越界,将较小的数依次append进temp
if li[i] > li[j]:
temp.append(li[j])
j += 1
else:
temp.append(li[i])
i += 1
# 上述while执行完,两个部分肯定有一个部分为空,越界了
while i <= mid: # 若左部分仍有数即 i<=mid且(j>high越界了),那么将左部分剩下的有序数依次append进temp,此时temp为升序列表
temp.append(li[i])
i += 1
while j <= high: # 此时左部分为空即越界了,将右部分剩下的有序数append进temp
temp.append(li[j])
j += 1
li[low:high+1] = temp
def merge_sort(li, low, high):
mid = (low+high)//2
if low < high: # 递归结束条件
merge_sort(li, low, mid)
merge_sort(li, mid+1, high)
merge(li, low, mid, high)
b = [5,3,4,6,2,1,7]
merge_sort(b, 0, len(b)-1)
print(b)
为了方便大家理解递归的过程,我把他整理了出来。‘’纸上得来终觉浅,绝知此事要躬行‘’,同时也是建议还没理解的同学自己拿起笔运算一下递归的过程,很值得回味!
下面是归并排序的递归过程,以 b = [5,3,4,6,2,1,7] 为例
若您对上述三种排序代码思路仍模糊,请您前往b站的**python版快速、堆、归并排序视频讲解**,我认为该视频讲的挺好的。
![点赞](https://i-blog.csdnimg.cn/blog_migrate/e18a2d80c5086325d3681b9092234afe.png)