快速排序
快排思路:
1.取一个元素p(第一个元素),使元素p归位
2.列表被p分成两部分,左边都比p小,右边都比p大
3.递归完成排序
算法关键点:
1.整理
2.递归
总共分为logn层,每层一次partition,复杂度为:O(nlogn)
# 添加装饰器 查看排序所需要的时间
def cal_time(func):
def wrapper(*args,**kwargs):
t1 = time.time()
result = func(*args, **kwargs)
t2 = time.time()
print("%s running time: %s secs." %(func.__name__,t2 - t1))
return result
return wrapper
# 分割序列的函数
def partition(li,left,right):
item = li[left]
while left < right:
while left < right and li[right] >= item:
right -= 1
li[left] = li[right]
while left < right and li[left] < item:
left += 1
li[right] = li[left]
li[left] = item # left == right
return left
partition()函数,右手左手重复动作
# 递归的函数
def quicksort(li,left,right):
if left < right: # 递归终止条件
mid = partition(li,left,right)
quicksort(li,left,mid-1)
quicksort(li,mid+1,right)
@cal_time
def quick_sort(li):
quicksort(li,0,len(li)-1)
python默认最大递归深度为1000,可以通过sys模块修改最大递归深度
import sys
sys.setrecursionlimit(100000) # 将递归深度修改为 100000
快排时间复杂度:最好O(nlogn) 平均O(nlogn) 最坏(n^2)
堆排序
在学习堆排序之前需要首先学习几个概念:
二叉树:度不超过2的树(节点最多有两个叉),二叉树的存储方式有链式存储方式和顺序存储方式,本次学习使用顺序存储方式(列表)。
父节点和左孩子节点的编号下标关系:i 与 2i+1 (i节点从编号0开始)
父节点与右孩子节点的编号下标关系:i 与 2i+2
大根堆:一棵完全二叉树,满足任一节点都比其孩子节点大
小根堆:一棵完全二叉树,满足任一节点都比其孩子节点小
当根节点的左右子树都是堆时,可以通过一次向下调整来将其变换成一个堆
堆排序过程:
lwst = [5,3,8,1,2,4]
# 调整函数
def sift(list,low,high):
i = low
j = 2 * i + 1
item = list[i]
while j <= high: # 孩子在堆里
# j < high说明 根节点有右孩子,若< 换成> 调整成小根堆
if j < high and list[j+1] > list[j]:
j = j+1 # j指向右孩子
if item < list[j]: # < 换成> 变成小根堆
list[i] = list[j]
i = j # 将调上去的位置作为根节点
j = 2 * i + 1 # 若有孩子继续调整
else:
break
list[i] = item
def heap_sort(list):
# 建堆 往上调整堆
n = len(list) # 共有n个元素
# 从最后一个元素往上调整,最后一个节点的父亲编号为n // 2 - 1
for i in range(n // 2 - 1,-1,-1):
sift(list,i,n-1)
# 大根堆已经建好
# 开始向下调整堆
for i in range(n-1,-1,-1):
list[i] ,list[0] = list[0], list[i] # 节省内存
sift(list,0,i-1)
# 向下调整堆的第二种方式
# li = []
# for i in range(n-1,-1,-1):
# li.append(li[0])
# li[0] = list[i]
# sift(list,0,i-1)
heap_sort(lwst)
print(lwst)
堆排序时间复杂度 O(nlogn)
归并排序
归并思想:
# 将两个有序列表进行排序
def merge(list,low,mid,high):
i = low # 第一段的开始编号
j = i + 1 # 第二段的开始编号
li = []
while i <= mid and j <= high:
if list[i] < list[j]:
li.append(list[i])
i +=1
else:
li.append(list[j])
j +=1
while i <= mid:
li.append(list[i])
i +=1
while j <= high:
li.append(list[j])
j += 1
list[low:high+1] = li # 将li列表写回list中
# 分解与合并函数
def merge_sorts(list,low,high):
if low < high: # 递归终止条件
mid = (low + high) // 2
merge_sorts(list,low,mid)
merge_sorts(list,mid+1, high)
merge(list,low,mid,high)
@cal_time
# 递归函数不能直接加装饰器,耗费资源严重
def merge_sort(list):
merge_sorts(list,0,len(list) - 1)
归并排序的时间复杂度 O(nlogn)
一般情况下:就运行时间而言:快排 < 归并排序 < 堆排序
希尔排序
def shell_sort(list):
n = len(list)
gap = n // 2 # 第一次指定步长为n // 2
while gap > 0: # 控制使gap直到为1,程序结束
for i in range(gap,n): # 这个for循环 本质是插入排序的过程
item = list[i]
j = i - gap
while j >= 0 and list[j] > item:
list[j+gap] = list[j]
j -= gap
list[j + gap] = item
gap //= 2 # 重新指定步长
希尔排序时间复杂度 O(n^1.3) , O(n^1.3) > O(nlogn),所以不如上述三种排序。