最近整理了面试笔试中经常出现的九大排序:冒泡排序(bubble)、选择排序(select)、插入排序(insert)、希尔排序(shell)、堆排序(heap)、快速排序(quick)、合并排序(merge)、基数排序(radix)、桶排序(bucket)。根据是否利用比较的思想进行排序,可将这些排序分为两大类。基于比较的排序:冒泡排序、选择排序、插入排序、希尔排序、堆排序、快速排序、合并排序;不基于比较法排序:基数排序、桶排序。根据是否稳定,可将其分为两大类。稳定排序:冒泡排序、插入排序、合并排序、基数排序、桶排序。不稳定排序:选择排序、希尔排序、堆排序、快速排序。这里的稳定是指:在待排序的文件中,若存在多个关键字相同的记录,经过排序后这些具有相同关键字的记录之间的相对次序保持不变,就称该排序是稳定排序;否则,是不稳定排序。
九大排序时间复杂度和空间复杂度如下表所示:
排序类别 | 时间复杂度 | 空间复杂度 |
冒泡排序 | 最差、平均都是O(n^2), 最好是O(n), | 1 |
插入排序 | 最差、平均都是O(n^2), 最好是O(n), | 1 |
选择排序 | 最差、平均都是O(n^2), | 1 |
希尔排序 | O(nlogn), | 1 |
堆排序 | O(nlogn), | 1 |
快速排序 | 平均O(nlogn) 最坏(O(n^2)) | O(logn) |
合并排序 | O(nlogn), | O(n) |
基数排序 | O(dn)(d是数组中最大数的位数) | O(n) |
桶排序 | O(n) | O(n+m)(m为桶的个数) |
九大排序源码如下:
#include "stdafx.h"
#include <iostream>
#include <vector>
using namespace std;
#define N 10
struct Bucket
{
vector<int> data;//桶中数据
};
void Swap(int& a,int &b)//交换函数,排序中经常用到的函数
{
int tmp=a;
a=b;
b=tmp;
}
//*******************************************************(1)快速排序*************************//
int Partion(int* data,int start,int end)
{
if (data==NULL||start>end||start<0||end<0)
{
return -1;
}
int key=data[start];
int i=start,j=end;
while (i<j)
{
do i++;while(data[i]<key);
while(data[j]>key) j--;
if (i<j) Swap(data[i],data[j]);
}
if (start!=j)//这时的j已经发生变化了
{
Swap(data[start],data[j]);
}
return j;
}
void QuickSort(int *data,int start,int end)
{
if(start<end)
{
int j=Partion(data,start,end);
QuickSort(data,start,j-1);
QuickSort(data,j+1,end);
}
}
void QuickSort(int *data,int len)
{
if (data==NULL||len<=1)
{
return ;
}
QuickSort(data,0,len-1);
}
//*******************************************************快速排序*************************//
//***************************************************(2)桶排序**************************//
void Bucket_sort(int *data,int len)
{
int imax,imin,num,pos;
int i,j,k;
imax=imin=data[0];
for ( i=1;i<len;i++)
{
if (data[i]>imax)
{
imax=data[i];
}
else
if (data[i]<imin)
{
imin=data[i];
}
}
num=(imax-imin+1)/N+1;
vector<Bucket> bk(num);
for (i=0;i<len;i++)
{
k=(data[i]-imin+1)/N;
bk[k].data.push_back(data[i]);
}
pos=0;
for (i = 0; i < num; i++)
{
int ilen=bk[i].data.size();
if (ilen>1)
{
QuickSort(&bk[i].data[0],ilen);
}
for (j = 0; j < ilen; j++)
{
data[pos++] = bk[i].data[j];
}
}
}
//***************************************************桶排序**************************//
//**************************************************(3)基数排序*****************************//
//辅助函数:求最大数的位数
int maxbit(int data[],int n)
{
int d=1;
int p=10;
for (int i=0;i<n;i++)
{
while(data[i]>=p)
{
p*=10;
++d;
}
}
return d;
}
//基排序,有点像桶排序
void radixSort(int data[],int n)
{
int d=maxbit(data,n);//最大数的位数
int *tmp=new int[n];
int *count=new int[10];//各个数位上0-9出现的次数
int i,j,k;
int radix=1;
for (i=1;i<=d;i++)
{
for (j=0;j<10;j++)
{
count[j]=0;
}
for (j=0;j<n;j++)//O(n)时间复杂度
{
k=(data[j]/radix)%10;//桶的编号,也就是要求的当前位上的数字
count[k]++;
}
for (j=1;j<10;j++)
{
count[j]=count[j-1]+count[j];//个数累加
}
for (j=n-1;j>=0;j--)//按照选择基数求余的结果进行排序,注意必须是递减次序遍历
{
k=(data[j]/radix)%10;
tmp[count[k]-1]=data[j];//tmp的数据个数跟data是一样的
count[k]--;
}
for (j=0;j<n;j++)
data[j]=tmp[j];
radix=radix*10;
for (int i=0;i<10;i++)
{
cout<<data[i]<<"\t";
}
cout<<endl;
}
delete[] tmp;
delete[] count;
}
//**************************************************基数排序*****************************//
//*******************************(4)堆排序**************************************
void HeapAjust(int array[],int i,int len)
{
int nChild;
int nTemp;
for (nTemp=array[i];2*i+1<len;i=nChild)
{
nChild=2*i+1;
if (nChild<len-1&&array[nChild+1]>array[nChild])
++nChild;
if (nTemp<array[nChild])
{
Swap(array[i],array[nChild]);
}
else
break;
}
}
void HeapSort(int array[],int len)
{
//建立一个最大堆
for (int i=len/2-1;i>=0;i--)
{
HeapAjust(array,i,len);
}
//堆调整
for (int i=len-1;i>0;i--)
{
Swap(array[i],array[0]);
HeapAjust(array,0,i);
}
}
//*******************************堆排序**************************************//
//*******************************(5)冒泡排序**************************************
void Buble_Sort(int data[],int len)
{
if (data==NULL||len<=1)
{
return;
}
for (int i=0;i<len-1;i++)
{
for (int j=0;j<len-i-1;j++)
{
if (data[j]>data[j+1])
{
Swap(data[j],data[j+1]);
}
}
}
}
//*******************************冒泡排序**************************************//
//*******************************(6)选择排序**************************************//
//算法思想:每趟在n-i+1个记录中选择最小的记录作为第i个记录的值
void Select_Sort(int data[],int len)
{
if (data==NULL||len<=1)
{
return;
}
for (int i=0;i<len-1;i++)
{
int key=i;
for (int j=i+1;j<len;j++)
{
if (data[j]<data[key])
{
key=j;
}
}
Swap(data[i],data[key]);
}
}
//*******************************选择排序**************************************//
//*******************************(7)插入排序**************************************//
//算法思想:在有序的前i个记录中插入一个值使这i+1个记录仍然有序
void Insert_Sort(int data[],int len)
{
if (data==NULL||len<=1)
{
return;
}
for (int i=1;i<len;i++)
{
int j=i;
int key=data[i];
while(j>=1&&key<data[j-1])
{
data[j]=data[j-1];
j--;
}
data[j]=key;
}
}
//*******************************插入排序**************************************//
//*******************************(8)希尔排序**************************************//
void Shell_Sort(int data[],int len)
{
if (data==NULL||len<=1)
{
return;
}
int d;//增量
for (d=len/2;d>=1;d/=2)
{
for (int i=d;i<len;i+=d)
{
int j=i;
int key=data[i];
while(j>=d&&key<data[j-d])
{
data[j]=data[j-d];
j-=d;
}
data[j]=key;
}
}
}
//*******************************希尔排序**************************************//
//*******************************(9)合并排序**************************************//
void Merge(int data[],int left,int mid,int right)
{
if (left<0||mid<0||right<0||(!(left<=mid&&mid<=right)))
{
return;
}
int i,j,k;
int *tmp=new int[right-left+1];
i=left;j=mid+1;
k=0;
while(i<=mid&&j<=right)
{
if (data[i]<data[j])
{
tmp[k++]=data[i++];
}
else
tmp[k++]=data[j++];
}
while(i<=mid) tmp[k++]=data[i++];
while(j<=right) tmp[k++]=data[j++];
for (k=0,i=left;i<=right;i++)
{
data[i]=tmp[k++];
}
delete[] tmp;
}
void MergeSort(int data[],int left,int right)
{
if (left<right)
{
int mid=left+((right-left)>>1);//注意不要写成:int mid=(left+right)/2;或者int mid=left+(right-left)>>1;
MergeSort(data,left,mid);
MergeSort(data,mid+1,right);
Merge(data,left,mid,right);
}
}
void Merge_Sort(int data[],int len)
{
if (data==NULL||len<=1)
{
return;
}
MergeSort(data,0,len-1);
}
//***********************************合并排序**************************************//
int _tmain(int argc, _TCHAR* argv[])
{
int data[11]={12,23,9,2,2,7,26,1,1,6,8};
int len=sizeof(data)/sizeof(int);
/*Bucket_sort(data,len);
QuickSort(data,len);
radixSort(data,len);
HeapSort(data,len);
Buble_Sort(data,len);
Select_Sort(data,len);
Insert_Sort(data,len);
Insert_Sort(data,len);*/
Merge_Sort(data,len);
for (int i=0;i<len;i++)
{
cout<<data[i]<<"\t";
}
cout<<endl;
return 0;
}
排序选择总结:
1.若n较小(如n<=50),可采用插入或选择排序
2.若数据初始状态基本有序,应选用插入、冒泡排序或快速排序;
3若n较大,则选用时间复杂度为线性的排序算法:如快速排序、堆排序、合并排序;其中快速排序是基于比较的内部排序中最好的办法,平均时间最短;堆排序所需辅助空间小于快排;而合并排序则是三者中唯一稳定的排序。
4.基数排序和桶排序往往应用在海量数据的排序上,比如n是几亿甚至几十亿,内存一次储存不了等。
实际应用中,应结合各种排序的优缺点,选择最适合的一个。