主定理

在算法分析中,主定理(英语:master theorem)提供了用渐近符号(大O符号)表示许多由分治法得到的递推关系式的方法
递归式子中三种情况:大部分操作(消耗时间)运行在根节点上,叶节点上或递归树的各行之间。

情形一

如果存在常数 $\epsilon >0$,有
$ f(n)=O\left(n^{\log _{b}(a)-\epsilon }\right) $ (多项式地小于)
那么
$ T(n)=\Theta \left(n^{\log _{b}a}\right) $

情形二

如果存在常数 k ≥ 0,有
$ f(n)=\Theta \left(n^{\log _{b}a}\log ^{k}n\right)$
那么
$T(n)=\Theta \left(n^{\log _{b}a}\log ^{k+1}n\right)$

情形三

如果存在常数$ \epsilon >0$,有
$f(n)=\Omega \left(n^{\log _{b}(a)+\epsilon }\right)$(多项式地大于)
同时存在常数$ c<1$以及充分大的 $n$,满足
$af\left(\frac {n}{b}\right)\leq cf(n)$
那么
$T\left(n\right)=\Theta \left(f\left(n\right)\right)$

归纳(induction)、递归(recursion)及 归简(reduction)

归简法是指将某一问题转换称另一个问题。如将一个未知问题转换称一个已解决问题。
归纳法则用于证明某个语句对于大型对象类是否成立。
递归法则用于函数自我调用时。

排序算法

冒泡排序

在无序区查找最大元素放置最后,循环n次,复杂度$O(n^{2})$,稳定,$O(1)$。

1
2
3
4
5
def bubble_sort(seq):
for i in range(len(seq)-1,0,-1):
for j in range(i-1,0,-1):
if seq[i]<seq[j]:
seq[j],seq[i]=seq[i],seq[j]

选择排序

$O(n^{2})$,对数组稳定(链表不稳定),$O(1)$。

1
2
3
4
5
6
def sel_sort(seq):
for i in range(len(seq)-1,0,-1):
max_j=i
for j in range(i):
if seq[j]>seq[max_j]: max_j=j
seq[i],seq[max_j]=seq[max_j],seq[i]

插入排序

假设前 n-1 个数据已排好序,现在要将 n 插入到合适的位置(将无序区的元素插入到有序区的合适位置)。$O(n^{2})$,稳定,$O(1)$。

1
2
3
4
5
6
def ins_sort(seq):
for i in range(1,len(seq)):
j=i
while j>0 and seq[j-1]>seq[j]:
seq[j-1],seq[j]=seq[j],seq[j-1]
j-=1

归并排序

将问题归简为两个相等子问题,依次递归。$O(n\log n)$,空间复杂度 $O(n)+O(\log n)$(从上往下)$O(1)$(从下往上),稳定。

1
2
3
4
5
6
7
8
9
10
11
12
13
def mergesort(seq):
mid=len(seq)//2
lft,rgt=seq[:mid],seq[mid:]
if len(lft)>1:lft=mergesort(lft)
if len(rgt)>1:rgt=mergesort(rgt)
res=[]
while lft and rgt:
if lft[-1]>=rgt[-1]:
res.append(lft.pop())
else :
res.append(rgt.pop())
res.reverse()
return (lft or rgt)+ res

计数排序

统计每一个数字出现的次数。 $O(n+m)$,稳定,$O(n+m)$。

1
2
3
4
5
6
7
8
from collections import defaultdict
def counting_sort(seq,key=lambda x:x):
B,C=[],defaultdict(list)
for x in seq:
C[key(x)].append(x)
for k in range(min(C),max(C)+1):
B.extend(C[k])
return B

基数排序

对数字的每一位进行排序。$O(k\times n)(平均) O(n^{2})(最坏)$,稳定,$O(n)$。

1
2
3
4
5
6
7
8
9
10
import math
def radix_sort(seq,radix=10):
K=int(math.ceil(math.log(max(seq)+1,radix)))
for i in range(1,K+1):
bucket=[[] for i in range(radix)]
for val in seq:
bucket[val%(radix**i)//(radix**(i-1))].append(val)
del seq[:]
for each in bucket:
seq.extend(each)

堆排序

使用堆结构的性质。$O(n\log n)$,不稳定,$O(1)$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def heap_sort(seq):
def sift_down(start,end):
root=start
while True:
child=2*root+1
if child>end:
break
if child+1<=end and seq[child]<seq[child+1]:
child+=1
if seq[root]<seq[child]:
seq[root],seq[child]=seq[child],seq[root]
root=child
else:
break
for start in range((len(seq)-2)//2,-1,-1):
sift_down(start,len(seq)-1)
for end in range(len(seq)-1,0,-1):
seq[0],seq[end]=seq[end],seq[0]
sift_down(0,end-1)
return seq

快速排序

分解为两个子问题,时间代价主要在分解上,$left<right$。$O(n\log n)(平均) O(n^{2})(最坏)$,不稳定,$O(\log n)$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def quicksort(seq,left,right):
if left<right:
p=partition(seq,left,right)
quicksort(seq,left,p)
quicksort(seq,p+1,right)
return
def partition(seq,lft,rgt):
pivot=seq[rgt-1]
i=lft-1
for j in range(lft,rgt):
if seq[j]<pivot:
i+=1
seq[i],seq[j]=seq[j],seq[i]
if seq[rgt-1]<seq[i+1]:
seq[i+1],seq[rgt-1]=seq[rgt-1],seq[i+1]
return i+1

希尔排序

插入排序的进化版。 $O(n\log ^{2}n)(平均) O(n^{2})(最坏) $,不稳定,$O(1)$.

1
2
3
4
5
6
7
8
9
10
11
12
13
def shell_sort(seq):
n=len(seq)
gap=n//2
while gap>0:
for i in range(gap,n):
temp=seq[i]
j=i
while j>=gap and seq[j-gap]>temp:
seq[j]=seq[j-gap]
j-=gap
seq[j]=temp
gap=gap//2
return seq

桶排序

数据分为几组。组间有序,组内再排序。 $O(n)$,稳定,$O(m)$.

1
2
3
4
5
6
7
8
9
def bucket_sort(seq,n=10):
buckets=[[]for i in range(n)]
for i in range(0,len(seq)):
buckets[seq[i]//n].append(seq[i])
for i in range(0,n):
buckets[i]=mergesort(buckets[i])
del seq[:]
for x in buckets:
seq.extend(x)

参考资料

  1. https://zh.wikipedia.org/wiki/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95
  2. 《python算法教程》