一、插入排序(扑克牌原理) n^2 原址
A[1,...,n-1]是排好序的,把第n个往前<-方向比较,比较一个移一个,最后在停下来的位置插入A[n]。o(n^2)
二、分治法(归并) nlgn 非原址
//先分解
SORT(A,p,r)
if p<r
q=[(r-p)/2]
SORT(A,p,q)
SORT(A,q+1,r)
MERGE(A,p,q,r)
MERGE(A,p,q,r)
L[1...q-p+1]=A[p...q]
R[1...r-q+1]=A[q+1...r]
//哨兵
L[q-p+2]=无穷
R[r-q+2]=无穷
for i=p:r
A[i]=max(L[a],R[b])
a++;//or b++;
三、分治法--最大子数组问题
描述:寻找A的和最大的非空连续子数组
解决:按中点分成两段,递归找左右段各自的最大段,再找跨越中点的最大段,三个结果进行比较
FIND(A,low,high)
if high==low
return(low,high,A[low])
else mid=[(low+high)/2]
(l-low,l-high,l-sum)=FIND(A,low,mid)
(r-low,r-high,r-sum)=FIND(A,mid+1,high)
(c-low,c-high,c-sum)=CROSS-FIND(A,low,mid,high)
choose the max one to return
CROSS-FIND(A,low,mid,high)//这是跨中点段
left_sum=MAX
sum=0
for i=mid:low
sum+=A[i]
if(sum>left_sum)
left_sum=sum
max_left=i
right_sum=MAX
sum=0
for i=mid+1:high
sum+=A[i]
if(sum>right_sum)
right_sum=sum
max_right=i
return(max_left,max_right,left_sum+right_sum)
四、矩阵乘法的Strassen算法
描述:AxB按照原始的乘法需要递归三层,现在:
把矩阵分解成n/2 x n/2 的形式:A11,A12,A21,A22,B11,B12,B21,B22;
再将其两两加or减 得10个矩阵S1,S2,...,S10:
S1=B12-B22 S2=A11+A12 S3=A21+A22 S4=B21-B11 S5=A11+A22
S6=B11+B22 S7=A12-A22 S8=B21+B22 S9=A11-A21 S10=B11+B12
再递归的计算7次乘法。。。。。。
五、随机算法(优先级 原址)
1. A[1...n],给每一个数分配一个优先级,P[i]=RANDOM(1,n^3);使用P各个数的大小对A进行排序//问:若产生的优先数相等怎么办?--对其再进行随机数赋值
2. 原址排列给定数列;A[i]是从i--n数中随机选取一个j,swap(i,j),然后不再改变
六、堆排序 nlgn 非原址
二叉堆--被近似地看成完全二叉树;分为最大堆、最小堆;;建堆,维护堆,由堆对一个数组进行原址排序。
//对LEFT[i],RIGHT[i]满足最大堆,而A[i]不一定大于孩子的维护
MAX_HEAPIFY(A,i){
//让A[i]与左孩子A[2i],A[2i+1]比较,选出大的放到顶上
if(2i==A.heapsize)right=i;
else if(2i>A.heapsize)left=i;right=i;
else left=2i;right=2i+1;
max=MAX(A[i],A[left],A[right]);
if max==A[i] return;
else if max==A[left] exchange(A[i],A[left]) r=left
else if max==A[right] exchange(A[i],A[right]) r=right
MAX_HEAPIFY(A,r)
}
//建堆
BUILD_MAX_HEAP(A){
//从最后一个非叶子结点开始
A.heapsize=A.length
for i=[A.length/2]:1
MAX_HEAPIFY(A,i);
}
//用堆排序
HEAPSORT(A){
//先建堆
BUILD_MAX_HEAP(A)
//排好序的往堆尾放
for i=A.length:2
exchange(A[1],A[i])
A.heapsize--;
MAX_HEAPIFY(A,1)
}
//取出最大的元素 并维护堆
HEAP_EXTRACT_MAX(A){
if A.heapsize<1 error;
max=A[1]
A[1]=A[A.heapsize]
A.heapsize--
MAX_HEAPIFY(A,1)
return max
}
//在建好的堆中位置i插入key
HEAP_INCREASE_KEY(A,i,key){
if key<A[i]
A[i]=key
MAX_HEAPIFY(A,i)
else if key==A[i] return;
else
A[i]=key
while i>1&&A[i/2]<A[i]
exchange(A[i],A[i/2])
i=i/2
}
七、快速排序(也有分治思想)最坏n^2 期望时间:nlgn 原址稳定,是排大数组的常用算法
//快排的实现
QUICKSORT(A,p,r){
if p<r
q=PARTITION(A,p,r)
QUICKSORT(A,p,q-1)
QUICKSORT(A,q+1,r)
}
PARTITION(A,p,r){
x=A[r]
i=p-1
for j=1:r-1
if A[j]<x
i++
exchange(A[i],A[j])
exchange(A[i+1],A[r])
return i+1
}
快排的随机化版本,从p,r 之间随机选择一个数,与r 进行交换
快排的尾递归:
TAIL(A,p,r){
while p<r
q=PARTITION(A,p,r)
TAIL(A,p,q-1)
p=q+1
}
更细致的选取主元的方法:随机选三个数,取中间的数作为主元
以上是基于比较的排序方法,最坏情况的下界是O(nlgn),堆、归并是渐进最优
八、计数排序 zeta(k+n) 稳定 不是原址 输入数据都是一个小区间的整数
//A是输入数组,B是输出数组,C记录各元素应放位置
SORT(A,B,k){//k应该是最大元素数
for i=0:k
C[i]=0
for i=0:A.length-1
C[A[i]]++
for i=1:k
C[i]=C[i]+C[i-1]
for i=A.length-1:0
B[C[A[i]]=A[i]
C[A[i]]--
}
九、基数排序
描述:按最低有效位排序,合并后再按次低有效位排序,......;一位数排序算法最好是选取稳定的(计数常用)
十、桶排序
描述:把一个区间划分成几等分,各用一个list,然后落在各区间里的数分别排序,再把他们合并。
要求:元素服从均匀分布,平均情况下的时间代价为O(n)
十一、遗忘比较交换算法--列排序算法
描述:不依赖待排序元素的值,也不依赖之前比较的结果(0,1排序引理可证之);
包含n个元素的矩形数组的排序,有r行xs列,满足:
r必须是偶数;s|r;r>=2*s^2;
得到的结果是列优先有序的,从上到下,从左到右 单增。
十二、中位数和统计量
1.同时求最大/最小值--最多3[n/2]次
描述:总数为奇则把最大最小值的初始值设为first元素,为偶则取前两个比较后设为最大最小初值;剩余的数每两个相互比较,较小的再与min比,较大的再与max比。
2.同时求最大数和次大数
描述:把所有数两两配对取较大值,再次两两配对取较大值,...,最后得出的数为最大数;而次大数再从最大数开始回过去找,取最大值升上来的每个线路结点的孩子一一比较,最后得出次大值。
3.用快排找第i大的元素--期望为线性时间的选择算法
描述:只处理单边的快排
RANDOMIZED_SELECT(A,p,r,i){
if p==r
return A[r]
q=PARTITION(A,p,r)
k=q-p+1;//这是q号元素在队伍中的次序
if k==i
return A[q]
else if k>i
return RANDOMIZED_SELECT(A,p,q,i)
else return RANDOMIZED_SELECT(A,q+1,r,i-k)
}
4.最坏情况为线性时间的选择算法
描述:返回n>1时的第i小的元素;n=1时返回唯一值;
SELECT(A,p,r,i)
1.将n个元素划分为[n/5]组,每组5个,剩余元素为一组
2.寻找每一组的中位数;先用插入排序,然后取中位数;得B[k]
3.对得到的中位数们递归调用SELECT(B,0,k,k/2),返回值为x,它记录了中位数的中位数;
4.利用快排修改过的PARTITION(A,p,r,x),以x为主元对整个数组进行划分,然后得出x的位置在q标处
5.若i=q,则返回x;若i<q,则递归调用SELECT(A,p,q-1,i);若i>q,则递归调用SELECT(A,q+1,r,i-q)。