几种排序算法
插入排序
向已经排好序的序列依次插入元素
时间复杂度:
- 循环的嵌套,为 o(N^2)
- 如果序列已经被排好序,则为 o(N) (因为内部的’for’不起作用)
- 含有x个逆序对数,则需要做x次交换操作
- 含有N个元素的数组,最多有 N(N-1)/2个逆序对数,平均 N(N-1)/4个
- 算法的平均时间复杂度为 Ω(N^2)
代码如下:
void insert_sort(int a[],int num)//a为要排序的数组,num为数组元素个数,下同
{
int j,p,tmp;
for(p=1;p<num;p++)
{
tmp=a[p];
for(j=p;j>=1&&a[j-1]>tmp;j--)
{
a[j]=a[j-1];
}
a[j]=tmp;
}
}
shell排序
将区间拆分,间隔为increment,每隔increment,用insert排序
之前已经降低了逆序数,因此再用insert排序会快很多
希尔排序的时间复杂度取决于increment的选取
increment很差的情况下,有可能在increment>1是,均不进行排序,全部放到increment=0是完成
希尔increment的取法:h(t)=[N/2] h(t+1)=[h(t)/2]
时间复杂度的上界为 Θ(N^2)
Hibbard increment的取法: 1,3,7,…,2^k-1 (更好)
时间复杂度上界为 Θ(N^3/2)
代码如下:
void shell_sort(int a[],int num)
{
int i,j,increment,tmp;
for(increment=num/2;increment>0;increment/=2)
{
for(i=increment;i<num;i++)
{
tmp=a[i];
for(j=i;j-increment>=0&&a[j-increment]>tmp;j-=increment)
a[j]=a[j-increment];
a[j]=tmp;
}
}
}
堆排序
先建大根堆,后依次将堆顶与堆尾交换,完成新堆顶的下沉(以已排好的元素前一个元素为堆尾)
时间复杂度:
建堆 o(N)
堆的删除 每次o(log(N)) 总共o(Nlog(N))
总共 o(Nlog(N))
#define leftchild(i) (2*i+1)
void swap(int *a,int *b) //交换函数
{
int tmp;
tmp=*a;
*a=*b;
*b=tmp;
}
void percdown(int a[],int i,int num) //堆下沉排序,从第i号元素向下维护堆为大根堆
{
int child;
int tmp;
for(tmp=a[i];leftchild(i)<num;i=child)
{
child=leftchild(i);
if(child+1<num&&a[child]<a[child+1])//找两孩子中较大的孩子
child++;
if(tmp<a[child])//本质:插入排序
a[i]=a[child];
else break;
}
a[i]=tmp;
}
void heap_sort(int a[],int num)
{
int i;
for(i=num/2-1;i>=0;i--)//完成建堆 num/2-1:最后一个非终端结点
percdown(a,i,num);
for(i=num-1;i>0;i--)
{
swap(&a[0],&a[i]);/*删除最大元方法:将最大元(堆顶)与堆尾交换,其余部分堆下沉*/
percdown(a,0,i);
}
}
归并排序
void merge(int a[],int tmparr[],int lp,int rp,int re)
{
int le=rp-1;
int tp=lp;
int i,totalnum=re-lp+1;
while(lp<=le&&rp<=re)
{
if(a[lp]<=a[rp])
tmparr[tp++]=a[lp++];
else
tmparr[tp++]=a[rp++];
}
while(lp<=le)
tmparr[tp++]=a[lp++];
while(rp<=re)
tmparr[tp++]=a[rp++];
for(i=0;i<totalnum;i++,re--)
a[re]=tmparr[re];
}
void msort(int a[],int tmparr[],int left,int right)
{
int center;
if(left<right)
{
center=(left+right)/2; //分治
msort(a,tmparr,left,center);
msort(a,tmparr,center+1,right);
merge(a,tmparr,left,center+1,right);//合并
}
}
void merge_sort(int a[],int num)
{
int *tmparr;
tmparr=(int*)malloc(num*sizeof(int));
//共用一个tmparr,而不是分别在merge里建立tmparr,这样节省空间
//空间复杂度 o(N) 和o(Nlog(N)) 的区别
//问题:占用额外的线性内存(tmp),且copy耗时
//是大部分外排的基石
if(tmparr!=NULL)
{
msort(a,tmparr,0,num-1);
free(tmparr);
}
else
printf("No space for tmparr\n");
return;
}
快速排序
int median3(int a[],int left,int right)
{
int center=(left+right)/2;
//找中三数中中间的树
if(a[left]>a[center])
swap(&a[left],&a[center]);
if(a[left]>a[right])
swap(&a[left],&a[right]);
if(a[center]>a[right])
swap(&a[center],&a[right]);
//A[Right]肯定比枢轴A[Center]大,位于枢轴右侧,因此不需要参与后续的放枢轴左右的比较过程中
swap(&a[center],&a[right-1]);
return a[right-1];
}
void Qsort(int a[],int left,int right)
{
int i,j;
int pivot;
if(right-left>=3)
{
pivot=median3(a,left,right);
i=left;
j=right-1;
while(1)
{
while(a[++i]<pivot);
while(a[--j]>pivot);
if(i<j)
swap(&a[i],&a[j]);
else
break;
}
swap(&a[i],&a[right-1]);
Qsort(a,left,i-1);
Qsort(a,i+1,right);
}
else
insert_sort(a+left,right-left+1);
}
void quick_sort(int a[],int num)
{
Qsort(a,0,num-1);//接口统一
}