在这里,九九给大家比较一下四大排序的区别和优缺点,并把代码黏在下面,大家有需要自取,喜欢可以收藏哦~
- 收藏不点关注的都是小狗,小狗!
- 点赞不点关注的都是小猪,小猪!
时间复杂度
对于插入排序来说,一般情况下时间复杂度是O(n^2),而其他的三个排序算法时间复杂度是O(nlogn)。
但是这只是平均情况下,还有特殊情况。
如果对于基本有序的一组数据经行处理,那么插入排序会进化到O(nlogn)级别,而对于快速排序来说,则可能退化到O(n^2)级别。(为此快速排序优化了以时间种子选随机数来决定第一个元素——基数是谁,但是总体来说,还是没有插入排序优秀。)
而如果对于拥有大量相同元素的一组数据处理,归并排序就可能更占优势。但是快速排序还有一种优化算法——三路排序,直接有效地提高了排序的效率。
开辟新空间
归并排序需要重新开辟一份和所排数据大小一样的空间来进行排序。
而堆排序和插入排序只需要赋值的时候那一个额外空间,空间大小为O(1)。
对于快速排序,因为是一个归并的过程,额外空间就是归并的最大层数——O(logn)级别。
原地排序
归并排序必须开辟额外空间,来完成归并,才能完成排序。
而其他的排序都可以直接通过数组间交换元素来完成排序。
稳定性
什么叫稳定性呢?
其实很好理解,只不过是对于相等的元素来说,排序前和排序后,他们的位置不发生改变。
举个栗子:
4a 2 1 5 4b 8 3
排序之后稳定:1 2 3 4a 4b 5 8
排序之后不稳定:1 2 3 4b 4a 5 8
而插入排序和归并排序,都是稳定的排序。
快速排序和堆排序,都是不稳定的排序。
这个道理其实很简单,但也没办法讲,大家可以自己一步一步走走过程,会发现的确是这样。
总结
对于这四个算法来说,每种都有优缺点,但是比较之下,快速排序的优化发展的更顺利、更远,所以在有多种选择的时候,我们一般首选快速排序。
而如果确定了排序的对象需要考虑稳定性的话,通常选择归并排序。当然了,也可以写一个比较符号的运算符重载,以此来保证快速排序的稳定性。
而对于一些专门卡空间(很少见)的情况,对于时间放得更开的话,可以考虑插入排序和堆排序。
下面将四中算法贴在这里,喜欢请自取。(下次我一定写注释!这不是个好习惯)
//插入排序
#include <iostream>
#include <algorithm>
using namespace std;
template<typename T>
void insertionSort(T arr[], int n){
for( int i = 1 ; i < n ; i ++ ) {
T e = arr[i];
int j;
for (j = i; j > 0 && arr[j-1] > e; j--)
arr[j] = arr[j-1];
arr[j] = e;
}
return;
}
template<typename T>
void insertionSort(T arr[], int l, int r){
for( int i = l+1 ; i <= r ; i ++ ) {
T e = arr[i];
int j;
for (j = i; j > l && arr[j-1] > e; j--)
arr[j] = arr[j-1];
arr[j] = e;
}
return;
}
//归并排序
#include <iostream>
#include <algorithm>
using namespace std;
template<typename T>
void __merge(T arr[], int l, int mid, int r){
T aux[r-l+1];
for( int i = l ; i <= r; i ++ )
aux[i-l] = arr[i];
int i = l, j = mid+1;
for( int k = l ; k <= r; k ++ ){
if( i > mid ) { arr[k] = aux[j-l]; j ++;}
else if( j > r ){ arr[k] = aux[i-l]; i ++;}
else if( aux[i-l] < aux[j-l] ){ arr[k] = aux[i-l]; i ++;}
else { arr[k] = aux[j-l]; j ++;}
}
}
template<typename T>
void __mergeSort(T 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+1, r);
if( arr[mid] > arr[mid+1] )
__merge(arr, l, mid, r);
}
template<typename T>
void mergeSort(T arr[], int n){
__mergeSort( arr , 0 , n-1 );
}
//快速排序(三路排序)
#include <iostream>
#include <ctime>
#include <algorithm>
using namespace std;
template <typename T>
int _partition(T arr[], int l, int r){
swap( arr[l] , arr[rand()%(r-l+1)+l] );
T v = arr[l];
int j = l;
for( int i = l + 1 ; i <= r ; i ++ )
if( arr[i] < v ){
j ++;
swap( arr[j] , arr[i] );
}
swap( arr[l] , arr[j]);
return j;
}
template <typename T>
int _partition2(T arr[], int l, int r){
swap( arr[l] , arr[rand()%(r-l+1)+l] );
T v = arr[l];
int i = l+1, j = r;
while( true ){
while( i <= r && arr[i] < v )
i ++;
while( j >= l+1 && arr[j] > v )
j --;
if( i > j )
break;
swap( arr[i] , arr[j] );
i ++;
j --;
}
swap( arr[l] , arr[j]);
return j;
}
template <typename T>
void _quickSort(T arr[], int l, int r){
// if( l >= r )
// return;
if( r - l <= 15 ){
insertionSort(arr,l,r);
return;
}
int p = _partition2(arr, l, r);
_quickSort(arr, l, p-1 );
_quickSort(arr, p+1, r);
}
template <typename T>
void quickSort(T arr[], int n){
srand(time(NULL));
_quickSort(arr, 0, n-1);
}
//堆排序
template<typename T>
void __shiftDown(T a[],int n,int mm){
int m=mm;
int count=n;
while(m*2+1<count){
int k=m*2+1;
if(k+1<count&&a[k+1]>a[k]){
k++;
}
if(a[k]<=a[m])break;
swap(a[k],a[m]);
m=k;
}
}
template<typename T>
void heapSort(T arr[],int n){
for(int i=(n-1)/2;i>=0;i--){
__shiftDown(arr,n,i);
}
for(int i=n-1;i>0;i--){
swap(arr[0],arr[i]);
__shiftDown(arr,i,0);
}
}