最优复杂度:当输入数组就是排好序的时候,复杂度为O(n),而快速排序在这种情况下会产生O(n^2)的复杂度。
最差复杂度:当输入数组为倒序时,复杂度为O(n^2)。
适用:少量元素的数组。
ps:插入排序的复杂度和逆序对的个数一样,当数组倒序时,逆序对的个数为n(n-1)/2,因此插入排序复杂度为O(n^2)。
void insertionSort(int a[], int len){
for(int i = 1; i < len; i++){
int save;
int j = i - 1;
while(j >= 0 && a[j+1] < a[j]){
save = a[j+1];
a[j+1] = a[j];
a[j] = save;
j--;
}
}
}
特点:stable sort、In-place sort
思想:通过两两交换,像水中的泡泡一样,小的先冒出来,大的后冒出来。
最坏运行时间:O(n^2)
最佳运行时间:O(n^2)(可以进行改进使得最佳运行时间为O(n))
void swap(int *a, int *b){
int save;
save = *a;
*a = *b;
*b = save;
}
void bubbleSort(int a[], int len){
int save;
for(int i = 0; i < len; i++){
for(int j = i + 1; j < len; j++){
if(a[i] > a[j]){
swap(a+i, a+j);
}
}
}
}
【选择排序 Selection Sort】
void swap(int *a, int *b){
int save;
save = *a;
*a = *b;
*b = save;
}
void selectionSort(int a[], int len){
int min, minIndex;
for(int i = 0; i < len - 1; i++){
min = a[i];
minIndex = i;
for(int j = i + 1; j < len; j++){
if(min > a[j]){
min = a[j];
minIndex = j;
}
}
swap(a+i, a+minIndex);
}
}
【归并排序 Merge Sort】
特点:stable sort、Out-place sort
思想:运用分治法思想解决排序问题。
最坏情况运行时间:O(nlgn)
最佳运行时间:O(nlgn)
基本思想:归并排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
void merge(int a[], int p, int m, int q){
int lenl = m - p + 1;
int lenr = q - m;
int L[lenl], R[lenr];
for(int i = 0; i < lenl; i++){
L[i] = a[p+i];
}
for(int i = 0; i < lenr; i++){
R[i] = a[m+i+1];
}
int mark_a = 0;
int mark_b = 0;
for(int i = p; i <= q; i++){
if(mark_a > lenl - 1){
a[i] = R[mark_b];
mark_b++;
}
else if(mark_b > lenr - 1){
a[i] = L[mark_a];
mark_a++;
}
else if(L[mark_a] <= R[mark_b]){
a[i] = L[mark_a];
mark_a++;
}
else{
a[i] = R[mark_b];
mark_b++;
}
}
}
void mergeSortRecursion(int a[], int p, int q){
int m;
if(p < q){
m = (p + q) / 2;
mergeSortRecursion(a, p, m);
mergeSortRecursion(a, m+1, q);
merge(a, p, m, q);
}
}
void mergeSort(int a[], int len){
mergeSortRecursion(a, 0, len-1);
}
【快速排序 Quick Sort】
特性:unstable sort、In-place sort。
最坏运行时间:当输入数组已排序时,时间为O(n^2),当然可以通过随机化来改进(shuffle array 或者 randomized select pivot),使得期望运行时间为O(nlgn)。
最佳运行时间:O(nlgn)
快速排序的思想也是分治法。
当输入数组的所有元素都一样时,不管是快速排序还是随机化快速排序的复杂度都为O(n^2)。在算法导论第三版的思考题7-2中通过改变Partition函数,可改进复杂度为O(n)。
基本思想:
1)选择一个基准元素,通常选择第一个元素或者最后一个元素;
2)通过一趟排序讲待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的 元素值比基准值大。
3)此时基准元素在其排好序后的正确位置
4)然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。
int partition(int a[], int p, int q){
int l = p + 1;
int r = q;
while(l < r){
if(a[l] > a[p] && a[r] < a[p]){
swap(a+l, a+r);
l++;
if(l == r){
swap(a + p, a + l - 1);
return l - 1;
}
r--;
}
else if(a[l] > a[p] && a[r] >= a[p]){
r--;
if(l == r){
swap(a + p, a + l - 1);
return l - 1;
}
}
else if(a[l] <= a[p] && a[r] < a[p]){
l++;
if(l == r){
swap(a + p, a + l);
return l;
}
}
else{
l++;
if(l == r){
swap(a + p, a + l - 1);
return l - 1;
}
r--;
}
}
if(a[l] < a[p]){
swap(a + p, a + l);
return l;
}
else{
swap(a + p, a + l - 1);
return l - 1;
}
}
void quickSortRecursion(int a[], int p, int q){
if(p < q){
int r = partition(a, p, q);
quickSortRecursion(a, p, r);
quickSortRecursion(a, r+1, q);
}
}
void quickSort(int a[], int len){
quickSortRecursion(a, 0, len - 1);
}
【堆排序】
特性:unstable sort、In-place sort。
最优时间:O(nlgn)
最差时间:O(nlgn)
此篇文章介绍了堆排序的最优时间和最差时间的证明:http://blog.csdn.net/xiazdong/article/details/8193625
思想:运用了最小堆、最大堆这个数据结构,而堆还能用于构建优先队列。
【计数排序 Counting Sort】
特性:stable sort、out-place sort。
最坏情况运行时间:O(n+k)
最好情况运行时间:O(n+k)
当k=O(n)时,计数排序时间为O(n)
void countingSort(int a[], int len){
int count[len];
int result[len];
memset(count, 0, sizeof(count));
memset(result, 0, sizeof(result));
for(int i = 0; i < len; i++){
for(int j = 0; j < len; j++){
if(j != i){
if(a[i] > a[j]) count[i]++;
}
}
}
for(int i = 0; i < len; i++){
while(result[count[i]] != 0){
count[i]++;
}
result[count[i]] = a[i];
}
for(int i = 0; i < len; i++){
a[i] = result[i];
}
}
【基数排序】
本文假定每位的排序是计数排序。
特性:stable sort、Out-place sort。
最坏情况运行时间:O((n+k)d)
最好情况运行时间:O((n+k)d)
当d为常数、k=O(n)时,效率为O(n)
int getNumInPos(int a, int pos){
while(pos > 1){
pos--;
a = a / 10;
}
a = a % 10;
return a;
}
void radixSort(int a[], int len){
int *arrays[10];
int highPos = 0;
for(int i = 0; i < 10; i++){
arrays[i] = (int *)malloc(sizeof(int) * (len + 1));//分配空间
arrays[i][0] = 0;//此位用于记录该数组储存数字的个数,初始化为零
}
int max = a[0];
int maxIndex = 0;
for(int i = 1; i < len; i++){
if(a[i] > max){
max = a[i];
maxIndex = i;
}
}
while(max > 0){
max = max / 10;
highPos++;
}
int num, index, count;
for(int i = 0; i < highPos; i++){
for(int j = 0; j < len; j++){
index = getNumInPos(a[j], i+1);
num = ++arrays[index][0];
arrays[index][num] = a[j];
}
count = 0;
for(int j = 0; j < 10; j++){
num = arrays[j][0];
for(int p = 1; p <= num; p++){
a[count++] = arrays[j][p];
}
arrays[j][0] = 0;
}
}
}
【桶排序】
假设输入数组的元素都在[0,1)之间。
特性:out-place sort、stable sort。
最坏情况运行时间:当分布不均匀时,全部元素都分到一个桶中,则O(n^2),当然[算法导论8.4-2]也可以将插入排序换成堆排序、快速排序等,这样最坏情况就是O(nlgn)。
最好情况运行时间:O(n)
参考博客(注意:文字和图片内容大都来自以下博客的提炼笔记,为了方便自己今后记忆。):
http://blog.csdn.net/xiazdong/article/details/8462393
http://blog.csdn.net/hguisu/article/details/7776068/