总是不能在网络上找到完整正确简洁的排序算法的python写法, 还是自己写一篇作为归纳.
下列方法都经过测试
测试方法:
import random
#生成随机数组
a = [random.randint(0,1000) for _ in range(100)]
#执行排序
sortfunc(a)
#判断结果
bool(a == sorted(a))
#若为降序排序则为bool(a == sorted(a,reverse=True))
1.选择排序
def selectsort(a):
for i in range(len(a)-1):
for j in range(i+1,len(a)):
if a[j] < a[i]:
a[i],a[j] = a[j],a[i]
记忆点:遇到比i位小的数,交换两个值,每一趟找出第i小的数
时间复杂度:O(n*n)
2.插入排序
2018.12.15 更新
根据算法导论重新写了插入排序,效率更高
版本1
def inssort(a):
for i in range(1,len(a)):
for j in range(i,0,-1):
if a[j-1] > a[j]:
a[j-1],a[j] = a[j],a[j-1]
else:
break
版本2
def inssort(a):
for i in range(1,len(a)):
key = a[i]
j = i-1
while j >= 0 and a[j] > key:
a[j+1] = a[j]
j -= 1
a[j+1] = key
自写版本
def inssort(a):
for i in range(1,len(a)):
key = a[i]
for j in range(i-1,-1,-1):
if a[j] > key:
a[j],a[j+1] = a[j+1],a[j]
时间复杂度:O(n*n)
3.冒泡排序
def bubble(a):
for i in range(len(a),0,-1):
swapped = False
for j in range(i-1):
if a[j+1] < a[j]:
a[j],a[j+1] = a[j+1],a[j]
swapped = True
if not swapped:
break
时间复杂度: O(n*n)
记忆点:
- 冒泡第i趟可以确保最左(或最右,取决于实现)i个值为最正确顺序;
- 加入swapped可以判断最好的情况,即数组已经排好序,时间复杂度降为O(n)
4.快速排序
def qsort(a,l,r):
if l < r:
p = a[r]
i = l - 1
for j in range(l,r):
if a[j] <= p:
i += 1
a[i],a[j] = a[j],a[i]
i += 1
a[i],a[r] = a[r],a[i]
qsort(a,l,i-1)
qsort(a,i+1,r)
在python快速排序的各种实现方法中有更详细一些的表述.
时间复杂度: O(nlogn),在每次选到的主元(pivot)都为当前数组最大或最小值时,算法退化到O(n*n).
5.归并排序
def merge(left, right):
res = []
while left and right:
if left[0] < right[0]:
res.append(left.pop(0))
else:
res.append(right.pop(0))
if left:
res.extend(left)
else:
res.extend(right)
return res
def mergesort(lists):
if len(lists) <= 1:
return lists
mid = len(lists)//2
left = mergesort(lists[:mid])
right = mergesort(lists[mid:])
return merge(left,right)
内部排序版本(在原数组上排序):
def merge(lst, l,mid,r):
left = lst[l:mid+1]
right = lst[mid+1:r+1]
k = l
while left and right:
if left[0] <= right[0]:
lst[k] = left.pop(0)
else:
lst[k] = right.pop(0)
k += 1
tail = left if left else right
for n in tail:
lst[k] = n
k += 1
def mergesort(lst,l,r):
if l < r:
mid = (l + r-1)//2
mergesort(lst,l,mid)
mergesort(lst,mid+1,r)
merge(lst,l,mid,r)
6.堆排序
def adjust_heap(a,i,size):
if i <= (size-2)//2:
l = 2*i + 1 #左结点
r = 2*i + 2 #右结点
m = i
if l < size and a[l] > a[m]:
m = l
if r < size and a[r] > a[m]:
m = r
if m != i:
#若子节点比根节点大,则交换结点
a[m],a[i] = a[i],a[m]
#交换后不能保证位于子节点位置的原本的"根节点"符合堆性质,继续调整
adjust_heap(a,m,size)
def build_heap(a):
size = len(a)
#倒数第一个下标为size-1,((size-1)-1)//为最后一个非叶节点
for i in range((size-2)//2,-1,-1):
adjust_heap(a,i,size)
def heapsort(a):
size = len(a)
#建堆
build_heap(a)
for i in range(size-1,0,-1):
#大顶堆性质:a[0]必为每次排序的最大值,将其放到数组后面
a[0],a[i] = a[i],a[0]
adjust_heap(a,0,i)
7.计数排序
def count_sort(a):
c = [0 for _ in range(1000)]
#若对于未知范围的数据 c = [0 for _ in range(max(a))]
for n in a:
c[n] += 1
res = []
for i,n in enumerate(c):
for k in range(n):
res.append(i)
return res
- 希尔排序
def shellsort(num):
step = len(num)//2
while step > 0:
for i in range(step, len(num)):
while i >= step and num[i-step] > num[i]:
num[i], num[i-step] = num[i-step], num[i]
i = i - step
step = step//2
return
时间复杂度: 对于N个范围在(0,K)的数,第一遍计数复杂度为O(n),第二遍重组目标数组复杂度为O(K),总时间复杂度为O(n+k).