一、 插入排序
InsertSort(A, p, q)
1 for i <- p+1 to q
2 do x <- A[i]
3 for j <- i-1 to p
4 do if A[j] > x
5 then A[j+1] <- A[j]
6 A[j+1] <- x
算法复杂度:O(n^2)
折半插入排序:总得算法复杂度没变,但是查找插入位置(比较)的时间减少了。算法复杂度:O(n^2)。
BInsertSort(SqList *L)
{
for(int i = 1; i < L.length(); ++i)
{
int low = 0, high = i-1;
int x = L[i];
while(low <= high)
{
int m = (low + high)/2;
if(x < L[m]) high = m-1;
else low = m+1;
}
for(int j = i-1; j >= high + 1; j--)
L[j+1] = L[j]
L[high+1] = x;
}
}
二、 希尔排序
ShellInsert(SqList &L, int dk)
{
for(int i = dk+1; i <= L.length(); ++i)
{
int x = L[i];
int j = i-dk;
while(j > 0 && L[j] > x) L[j+dk] = L[j];
L[j+dk] = x;
}
}
ShellSort(SqList &L, int delt[])
{
for(int i = 1; i < delt[0]; ++i)
ShellInsert(L, delt[i]);
}
希尔排序的复杂度难以分析。
三、 快速排序
int Partition1(A, p, r)
{
int x = A[r];
int i = p-1;
for(int j = p; j < r; ++j)
if(A[j] < x){
int tmp = A[++i];
A[i] = A[j];
A[j] = tmp;
}
A[r] = A[++i];
A[i] = x;
return i;
}
int Partition2(A, low, high)
{
int x = A[low];
while(low < high)
{
while(low < high && A[high] >= x)--high;
A[low] = A[high];
while(low < high && A[low] <= x) ++low;
A[high] = A[low];
}
A[low] = x;
return low;
}
int QuickSort(A, low, high)
{
if(low < high)
{
int m = Partition(A, low, high);
QuickSrot(A, low, m-1);
QuickSort(A, m+1, high);
}
}
快速排序的平均时间复杂度为O(nlongn),最坏情况下复杂度为O(n^2)
四、选择排序和堆排序
1. 简单选择排序
void SelectSort(A)
{
for(int i = 0; i < A.length(); ++i)
{
int j = SelectMin(A, i);
if(j != i)
{
int tmp = A[i];
A[i] = A[j];
A[j] = tmp;
}
}
}
简单选择排序的时间复杂度为O(n^2)
2. 树形选择排序:
需要较多的额外空间,与最大值进行比较等多余的计算。
3. 堆排序
//已知H[s..m]除H[s]外,都满足(大顶)堆的定义,将H[s...m]调整,使之满足堆的定义
HeapAdjust(H, s, m)
{
int key = H[s];
int i = s;
for(int j = s*2; j <= m; j *= 2)
{
if(j < m && H[j] < H[j+1])++j
if(key >= H[j])break;
H[i] = H[j];
i = j;
}
H[i] = key;
}
HeapSort(H)
{
for(int i = H.length/2; i > 0; --i)
HeapAdjust(H, i, H.length);
for(int i = H.length; i > 0; --i)
{
int tmp = H[i];
H[i] = H[1];
H[1] = tmp;
HeapAdjust(H, 1, i-1);
}
}
算法的时间复杂度为:O(nlogn),即使在
最坏情况下堆排序的
时间复杂度也为O(nlogn),仅需要一个辅助空间。
五、 归并排序
void Merge(int A[], int B[], int &C[])
{
for(int k = 0, i = 0, j = 0; i < A.length && j < B.length; ++k)
{
if(A[i] <= B[j]) C[k] = A[i++];
else C[k] = B[j++];
}
while(i < A.length)C[k++] = A[i++];
while(j < B.length)C[k++] = A[j++];
}
void MergeSort(int A[], int &C[], int m, int n)
{
if(m == n) C[m] = A[m];
else{
int mid = (m+n)/2;
MSort(A, C1, m, mid);
MSort(A, C2, mid+1, n);
Merge(C1,C2, C);
}
}
归并排序时间复杂度为O(nlogn),空间复杂度为O(n).
六、 基数排序
基数排序不需要关键字的比较。
1. 计数排序
- Count-Sort(A, B, k)
- 1 for i <-- 0 to k
- 2 do C[i] <-- 0
- 3 for i <-- 1 to length[A]
- 4 do C[A[i]] <-- C[A[i]] + 1
- 5 for i <-- 1 to k
- 6 do C[i] <-- C[i-1] + C[i]
- 7 for j <-- length[A] downto 1
- 8 do B[C[A[j]]] <-- A[j]
- 9 C[A[j]] <-- C[A[j]] - 1
计数排序时间代价:Θ(k+n), 实践中当k=O(n)时,运行时间为O(n)。
2. 基数排序
- Radix-Sort(A, d)
- 1 for i <-- 1 to d
- 2 do use a stable sort to sort array A on digit i
七、 内部排序算法比较
下面是一个总的表格,大致总结了我们常见的所有的排序算法的特点。
排序法 | 平均时间 | 最差情形 | 稳定度 | 额外空间 | 备注 |
冒泡 | O(n2) | O(n2) | 稳定 | O(1) | n小时较好 |
交换 | O(n2) | O(n2) | 不稳定 | O(1) | n小时较好 |
选择 | O(n2) | O(n2) | 不稳定 | O(1) | n小时较好 |
插入 | O(n2) | O(n2) | 稳定 | O(1) | 大部分已排序时较好 |
基数 | O(logRB) | O(logRB) | 稳定 | O(n) | B是真数(0-9), R是基数(个十百) |
Shell | O(nlogn) | O(ns) 1<s<2 | 不稳定 | O(1) | s是所选分组 |
快速 | O(nlogn) | O(n2) | 不稳定 | O(nlogn) | n大时较好 |
归并 | O(nlogn) | O(nlogn) | 稳定 | O(1) | n大时较好 |
堆 | O(nlogn) | O(nlogn) | 不稳定 | O(1) | n大时较好 |