稳定的
冒泡排序(bubble sort) — O(n^2)
鸡尾酒排序(Cocktail sort,双向的冒泡排序) — O(n^2)
插入排序(insertion sort)— O(n^2)
桶排序(bucket sort)— O(n); 需要 O(k) 额外空间
计数排序(counting sort) — O(n+k); 需要 O(n+k) 额外空间
合并排序(merge sort)— O(nlog n); 需要 O(n) 额外空间
原地合并排序— O(n^2)
二叉排序树排序 (Binary tree sort) — O(nlog n)期望时间; O(n^2)最坏时间; 需要 O(n) 额外空间
鸽巢排序(Pigeonhole sort) — O(n+k); 需要 O(k) 额外空间
基数排序(radix sort)— O(n·k); 需要 O(n) 额外空间
Gnome 排序— O(n^2)
图书馆排序— O(nlog n) with high probability,需要 (1+ε)n额外空间
不稳定的
选择排序(selection sort)— O(n^2)
希尔排序(shell sort)— O(nlog n) 如果使用最佳的现在版本
组合排序— O(nlog n)
堆排序(heapsort)— O(nlog n)
平滑排序— O(nlog n)
快速排序(quicksort)— O(nlog n) 期望时间,O(n^2) 最坏情况; 对于大的、乱数列表一般相信是最快的已知排序
Introsort— O(nlog n)
耐心排序— O(nlog n+ k) 最坏情况时间,需要 额外的 O(n+ k) 空间,也需要找到最长的递增子串行(longest increasing subsequence)
不实用的
Bogo排序— O(n× n!) 期望时间,无穷的最坏情况。
Stupid sort— O(n^3); 递归版本需要 O(n^2) 额外存储器
珠排序(Bead sort) — O(n) or O(√n),但需要特别的硬件
Pancake sorting— O(n),但需要特别的硬件
冒泡排序(bubble sort) — O(n^2)
鸡尾酒排序(Cocktail sort,双向的冒泡排序) — O(n^2)
插入排序(insertion sort)— O(n^2)
桶排序(bucket sort)— O(n); 需要 O(k) 额外空间
计数排序(counting sort) — O(n+k); 需要 O(n+k) 额外空间
合并排序(merge sort)— O(nlog n); 需要 O(n) 额外空间
原地合并排序— O(n^2)
二叉排序树排序 (Binary tree sort) — O(nlog n)期望时间; O(n^2)最坏时间; 需要 O(n) 额外空间
鸽巢排序(Pigeonhole sort) — O(n+k); 需要 O(k) 额外空间
基数排序(radix sort)— O(n·k); 需要 O(n) 额外空间
Gnome 排序— O(n^2)
图书馆排序— O(nlog n) with high probability,需要 (1+ε)n额外空间
不稳定的
选择排序(selection sort)— O(n^2)
希尔排序(shell sort)— O(nlog n) 如果使用最佳的现在版本
组合排序— O(nlog n)
堆排序(heapsort)— O(nlog n)
平滑排序— O(nlog n)
快速排序(quicksort)— O(nlog n) 期望时间,O(n^2) 最坏情况; 对于大的、乱数列表一般相信是最快的已知排序
Introsort— O(nlog n)
耐心排序— O(nlog n+ k) 最坏情况时间,需要 额外的 O(n+ k) 空间,也需要找到最长的递增子串行(longest increasing subsequence)
不实用的
Bogo排序— O(n× n!) 期望时间,无穷的最坏情况。
Stupid sort— O(n^3); 递归版本需要 O(n^2) 额外存储器
珠排序(Bead sort) — O(n) or O(√n),但需要特别的硬件
Pancake sorting— O(n),但需要特别的硬件
stooge sort——O(n^2.7)很漂亮但是很耗时
--摘自百度百科(排序算法)
冒泡排序:
(1)原版
for(int i = 0; i < n-1; i++)
for(int j = i; j < n;j++)
if(arr[j] > arr[j+1])
swap(arr[j],arr[j+1]);
选择排序:(每次选出第i小/大的数)
(1)原版
for(int i = 0; i < n; i++)
{
//选择当前最小的元素
minIndex = i;
for(int j = i+1; j < n; j++)
if(arr[j] < arr[minIndex])
minIndex = j;
swap(arr[i],arr[minIndex]);
}
插入排序:(每次跟前一个数据比较)
(1)原版
for(int i = 1; i < n; i++)
//寻找arr[i]合适的插入位置
for(int j = i; j > 0; j--)
if(arr[j] < arr[j-1])
swap(arr[j],arr[j-1]);
else
break;
(2)代码简化版(将判断条件放入了if语句中,节省了代码量)
for(int i = 1; i < n; i++)
for(int j = i; j > 0 && arr[j] < arr[j-1]; j--)
swap(arr[j],arr[j-1]);
(3)效率优化版(将复杂的交换换成了比较,直接赋值)
for(int i = 1; i < n; i++)
int e = arr[i];
for(int j = i; j > 0 && arr[j-1] > e; j--)//寻找e应该插入的位置
arr[j]=arr[j-1];
arr[j] = e;
总结:虽然插入排序时间复杂度为O(n2),但是在处理有规律的数据的时候用效率优化版的代码甚至可以达到O(n)的程度,因为数据有规律时其实只有少部分数据需要进行赋值操作,并且比较次数也减少了,而在现实生活中其实有很多数据是有规律的,所以用插入排序可以达到很好的效果。
希尔排序
(1)原版
void shell_sort(int arr[],int lenth)
{
int incre = lenth;
while(true)
{
incre/=2;
for(int k =0; k < incre;k++)//根据增量分为若干子序列
{
for(int i = k+incre;i<lenth;i+=incre)
{
for(int j = i; j>k; j-=incre)
{
if(arr[j] < arr[j-incre])
swap(arr[j],arr[j-incre]);
else
break;
}
}
}
if(incre==1)
break;
}
}
归并排序
(1)原版
void Merge(int arr[],int lenth)//调用归并排序
{
MergeSort(arr,0,n-1);
return;
}
int MergeSort(int arr[],int l, int r)//递归排序
{
if(l >= r)
return;
int mid = (l+r)/2;
MergeSort(arr,l,mid);
MergeSort(arr,mid,r);
merge(arr,l,mid,r);
}
void merge(int arr[],int l,int mid, int r)
{
int temp[r-l+1];
for(int i = l; i < r; i++)
temp[i-l] = arr[i];
int i = l, j = mid +1;
for(int k = l; k <= r; k++)
{
if(i > mid) //如果i和j超出范围,则直接赋值无需比较
{arr[k] = arr[j-l];j++;}
else if(j > r)
{arr[k] = arr[i-l];i++}
else if(temp[i-l] < temp[j-l])
{arr[k] = temp[i-1];i++;}
else
{arr[k] = temp[j-1];j++}
}
}
(2)优化
void Merge(int arr[],int lenth)//调用归并排序
{
MergeSort(arr,0,n-1);
return;
}
int MergeSort(int arr[],int l, int r)//递归排序
{
if(r-l < 15)//数量级小的时候,可以用插入排序来加速,降低大量递归带来的性能影响
{
insertionSort(arr,l,r);
return;
}
int mid = (l+r)/2;
MergeSort(arr,l,mid);
MergeSort(arr,mid,r);
//因为两边的数组有序,先判断边界值是否也有序然后在决定是否要合并,这样效率更高
if(arr[mid] > arr[mid+1])
merge(arr,l,mid,r);
}
void merge(int arr[],int l,int mid, int r)
{
int temp[r-l+1];
for(int i = l; i < r; i++)
temp[i-l] = arr[i];
int i = l, j = mid +1;
for(int k = l; k <= r; k++)
{
if(i > mid) //如果i和j超出范围,则直接赋值无需比较
{arr[k] = arr[j-l];j++;}
else if(j > r)
{arr[k] = arr[i-l];i++}
else if(temp[i-l] < temp[j-l])
{arr[k] = temp[i-1];i++;}
else
{arr[k] = temp[j-1];j++}
}
}
void insertionSort(int arr[], int l, int r)
{
int lenth = r-1+1;
for(int i =l+1; i < lenth; i++)
{
int e = arr[i]
for(int j = i; j > l && arr[j-1] > e; j--)
arr[j]=arr[j-1];
arr[j]= e;
}
}
(3)自底向上
void MergeBU(int arr[], int lenth)
{
for(int sz = 1; sz <lenth; sz+=sz)
for(int i = 0; i+sz< lenth; i+=sz+sz)
//对arr[i,···i+sz-1]和arr[i+sz,···i+sz+sz-1]进行归并
merge(arr,i,i+sz-1,min(i+sz+sz-1,n-1));//对数组边界进行限定,防止越界
}
void merge(int arr[],int l,int mid, int r)
{
int temp[r-l+1];
for(int i = l; i < r; i++)
temp[i-l] = arr[i];
int i = l, j = mid +1;
for(int k = l; k <= r; k++)
{
if(i > mid) //如果i和j超出范围,则直接赋值无需比较
{arr[k] = arr[j-l];j++;}
else if(j > r)
{arr[k] = arr[i-l];i++}
else if(temp[i-l] < temp[j-l])
{arr[k] = temp[i-1];i++;}
else
{arr[k] = temp[j-1];j++}
}
}
总结:归并排序先用二分法降低排序规模再进行排序达到了O(nlogn)的时间复杂度,在排序大量无序数据的时候,效率肯定是要比O(n2)的算法要高。不过原版的归并排序其实也有可以改进的地方,比如说在用递归拍好了两边数组的顺序后,如果能先判断一下这两个数组连起来是否已经有序,若无序在进行归并,有序则直接跳过归并数组的操作。而且因为两边数组已经有序,所以判断连起来是否有序只需判断边界值,一个if语句就能实现,在性能上是可以接受的。
另外一个优化的地方是数组长度为1的时候直接return,这样肯定会出现大量的递归操作,如果在长度比较小的时候,利用优化的插入排序直接排序,则可以避免这种损失。而且在较小规模的时候,插入排序算法确实能起到加速的作用!
快速排序
(1)原版
//对arr[l···r]进行partition操作
//返回索引p,使得arr[l···p-1]<arr[p],arr[l···p+1]>arr[p]
int partition(int arr[],int l,int r)
{
int k = arr[l];
int j = l;
for(i = l+1; i < r;i++)
if(arr[i] < k)
swap(arr[++k],arr[i]);
swap(arr[l],arr[j]);
return j;
}
//对arr[l,····r]进行快速排序
void quicksort(int arr[],int l,int r)
{
if(l>=r)
return;
int p = partition(arr,l,r);
quicksort(arr,l,p-1);
quicksort(arr,p+1,r);
}
void quick(int arr[],int lenth)
{
quicksort(arr,0,lenth-1 );
}
(2)优化
//对arr[l···r]进行partition操作
//返回索引p,使得arr[l···p-1]<arr[p],arr[l···p+1]>arr[p]
int partition(int arr[],int l,int r)
{
//随机选择第一个数,避免了算法复杂度退化为O(n2)
swap(arr[l],arr[rand()%(r-l+1)]);
int k = arr[l];
int j = l;
for(i = l+1; i < r;i++)
if(arr[i] < k)
swap(arr[++k],arr[i]);
swap(arr[l],arr[j]);
return j;
}
//对arr[l,····r]进行快速排序
void quicksort(int arr[],int l,int r)
{
if(r-l < 15)//数量级小的时候,可以用插入排序来加速,降低大量递归带来的性能影响
{
insertionSort(arr,l,r);
return;
}
int p = partition(arr,l,r);
quicksort(arr,l,p-1);
quicksort(arr,p+1,r);
}
void quick(int arr[],int lenth)
{
quicksort(arr,0,lenth-1 );
}
(3)再优化
//对arr[l···r]进行partition操作
//返回索引p,使得arr[l···p-1]<arr[p],arr[l···p+1]>arr[p]
int partition(int arr[],int l,int r)
{
//随机选择第一个数,避免了算法复杂度退化为O(n2)
swap(arr[l],arr[rand()%(r-l+1)]);
int k = arr[l];
//arr[l+1···i]<=k,arr[j···r]>=k
int i=l+1,j=r;
while(true)//这样处理是为了出现大量等于k的元素时,依旧能平均的分在两边,这样就能避免算法在处理大量重复元素的时候退化到O(n2)
{
while(i <=r && arr[i]<=k) i++;
while(j > l && arr[j]>=k) j--;
if(i > j) break;
swap(arr[i],arr[j]);
i++;
j--;
}
return j;
}
//对arr[l,····r]进行快速排序
void quicksort(int arr[],int l,int r)
{
if(r-l < 15)//数量级小的时候,可以用插入排序来加速,降低大量递归带来的性能影响
{
insertionSort(arr,l,r);
return;
}
int p = partition(arr,l,r);
quicksort(arr,l,p-1);
quicksort(arr,p+1,r);
}
void quick(int arr[],int lenth)
{
quicksort(arr,0,lenth-1 );
}
快速三路排序
//对arr[l···r]进行partition操作
//返回索引p,使得arr[l···p-1]<arr[p],arr[l···p+1]>arr[p]
int partition3ways(int arr[],int l,int r)
{
//随机选择第一个数,避免了算法复杂度退化为O(n2)
swap(arr[l],arr[rand()%(r-l+1)]);
int k = arr[l];
//arr[l+1···i]<=k,arr[j···r]>=k
int lt= l;//arr[l+1···lt]<k
int gt = r+1;arr[gt···r]>k
int i = l+1;//arr[lt+1···i]==k
while(i < gt)
{
if(arr[i] < k)
{swap(arr[++lt],arr[i]);i++;}
else if(arr[i]>k)
swap(arr[--gt],arr[i]);
else
i++;
}
swap(arr[l],arr[lt]);
}
//对arr[l,····r]进行快速排序
void quicksort3ways(int arr[],int l,int r)
{
if(r-l < 15)//数量级小的时候,可以用插入排序来加速,降低大量递归带来的性能影响
{
insertionSort(arr,l,r);
return;
}
int p = partition(arr,l,r);
quicksort3ways(arr,l,p-1);
quicksort3ways(arr,p+1,r);
}
void quick3ways(int arr[],int lenth)
{
quicksort3ways(arr,0,lenth-1 );
}