内排和外排:内排是指在排序的整个过程中,待排序的所有记录全部被放置在内存中。外排序是由于排序的记录个数太多,不能同时放在内存,整个排序过程需要在内外村之间多次交换数据才能进行。
1 冒泡排序:基本思想是两两比较相邻记录的关键字,如果反序则交换,最好情况为O(n),最坏情况为O(n^2). 稳定 空间复杂度O(1)
正宗的冒泡排序--从后向前循环,两重,
for(i =1;i <L.length;i++){
for(j =L.length-1;j>i:j++){
if(L[j] > L[j+1])
swap(L,j,j+1)
}
}
优化的冒泡排序--在基本为正序的序列中,没有必要进行多次循环。增加一个标记变量FLAG,如果j循环一遍没有发生任何数据交换的操作,则flag为false,算法结束
status flag = TRUE
for(i =1;i <L.length&&flag;i++){
flag = FALSE
for(j =L.length-1;j>i:j++){
if(L[j] > L[j+1]){
swap(L,j,j+1)
flag = TRUE
}
}
}
2 选择排序:通过n-i次关键字间的比较,从n-i+1个记录中选出关键字最小的记录,并和第i个记录交换
特点是交换移动的次数比较少,稳定 空间复杂度O(1)
for(i =1;i <L.length;i++){
min =i
for(j = i+1;j<=L.length;j++){
if(L[min] > L[j])
min = j
}
if(min != i)
swap(L,min,i)
}
3 插入排序: 基本操作--将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增1的有序表 稳定 空间复杂度O(1)
for(i=2;i <= L.length:i++){
if(L[i] < L[i-1]){
L[0] = L[i]
for(j = i-1;L[j]>L[0];j--)
L[j+1] = L[j]
L[j+1] = L[0]
}
}
以下均为时间复杂度为O(nlogn)
堆排序:数据结构为大顶堆和小顶堆,向下取整n/2是叶子节点的分界线。每次将堆顶的元素与堆尾的元素交换,剩下的n-1个序列重新构造成堆 不稳定 空间复杂度O(1) 当待排序序列个数较少的时候堆排序不建议使用,因为初始建堆比较次数很多
l两个部分:调整堆和主排序函数
for(i= n/2;i >0;i--){
HEAP-ADJUST(L,i,L.length)
}
for(i = L.length;i>1;i--){
swap(L,1,i)
HEAP-ADJUST(L,1,i-1)
}
HEAP-ADJUST(L,i,m){
int temp,j
temp = L[i] //存储父结点
for(j = 2i;j <=m;j++){
if(L[j]<L[j+1]&&j <m)
++j
if(temp >= L[j])
break;
L[s] = L[j]
s =j
}
L[s] = temp
}
4 快速排序--通过一趟排序将待排序记录分为独立的两部分,其中一部分关键字均比另一部分记录的关键字小,性能和取得枢轴元素有关。 不稳定,时间复杂度为O(1) 使用递归实现
QUICKSORT(L){
Qsort(L,1,L.length)
}
Qsort(L,low,high){
int pivot
if(low < high){
pilot = partition(L,low,high)
Qsort(L,low,pivot-1)
Qsort(L,pivot+1,high)
}
}
partition(L,high,low){
int pivotkey
pivotkey = L[low]
while(low < high){
while(low < high && L[high] >= pivotkey)
high --;
swap(L,low,high)
while(low< high && L[low] <=pivotkey)
low++;
swap(L,low,high)
}
return low;
}
partition 如果每次都分的很均匀,需要的时间是nlogn.最坏情况则是n^2
快速排序优化:随机化快排(随机选择low&high之间的一个元素,不要最左最右的元素)在期望意义上最优的算法
5 归并排序:基于分治的思想,用树实现,假如初始序列中含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到n/2个长度为2或者1的子序列,再两两归并,如此重复直到得到一个长度为n的有序数列为止, 稳定,O(nlogn),空间为O(n)
MergeSort(L){
Msort(L,1,L.length)
}
Msort(L,left,right){
if(left < right){
Msort(L,left,(left+right)/2)
Msort(L,(left+right)/2),right}
merge(L,left,(left+right)/2,right)
}
merge(L,left,middle,right){
int[] temp,int i,j,k
i = left j =middle k =0
while(i< =m&& j <=right){
if(L[i] <= L[j])
temp[k] = L[i]
i++;
else
temp[k] = L[j]
j++ ;
}
for(k = 0 ;k<=(right-left);k++){
L [left+k]= temp[k]
}
}
归并排序使用递归实现,有大量的递归开销,不用递归实现,需要额外的空间。
MergeSort(){
int len int[] L1
while(len < L.length){
Mergepass(L,L1,len)
len = len*2
Mergepass(L1,L,len
len = len*2)
}
Mergeapass(int[] L,int[] L1,int len){
int i =1
int j
while(i <= L.length-2len+1){
Merge(L,L1,i,i+len-1,i+2len-1)
i = i+2len+1}
if( i <n-s+1)
Merge(L,L1,i,i+s+1,L.length)}
else
for(j =i;j <= n;j++)
L1[j] = L[j]
}
6 希尔排序 并不是随便分组,各自排序,相隔某个增量的记录组成一个子序列,实现跳跃式的移动,在最后的一个增量必须是1.
适用于数列基本有序。空间复杂度为O(1)
ShellSort(int[] L,int len){
int increment = len,temp
do{
increment = increment/3+1
for(i =increment; i <len;i +=increment){
k =i
temp = L[k]
for(j = i-increment;;j >0&&L[j] >temp);j-=increment){
L[j+increment] = L[j]
k=j}
L[k] = temp
}
while(increment > 1)
}
7计数排序:元素分布在一定范围k内,时间复杂度紧确界(k+n),n是元素个数 , 稳定 不基于元素之间的比较
COUNTING-SORT(A,B,k)
for i 1to k
C[i] =0
forj=1 to length(A)
do C[A[j]] =C[A[j]]+1
for i =2 to length(C)
C[i ] = C[i-1]+C[i
for j = length(A) downto 1
do B[C[A[j]]] = A[j]
C[A[j]] = C[A[j]] -1
8 基数排序 按位排序,先排个位,十位,最后百位。如果内部嵌套的按位排序是稳定的则算法稳定。要求位数相同。当总数有n位时,分成lgn份计算最优。
RadixSort(A,n)
for i 1 to n
9 桶排序 假设数据服从均匀分布需要空间存放链表
BucketSort(A){
for i =0 to n-1
B[i] =0
for i=1 to n
A[i]插入链表B中
fori =0 to n-1
B[i]进行排序