您的位置:首页 > 其它

算法 基于比较的排序

2014-03-06 16:55 429 查看
插入排序

void insert_sort(int *ary,int n)
{
int temp;
int i,j;
for(i=1;i<n;i++)// 从第二个数开始
{
temp=ary[i];
for(j=i;j>0&&temp<ary[j-1];j--)
{
ary[j]=ary[j-1];
}
ary[j]=temp;
}
}

生活中可以用来模拟插入排序的事件,例如打牌中的将牌排序,十分形象具体

通常情况下插入排序O(N^2),基本有序(最好情况下)时是O(N),是稳定排序

折半插入排序可以在比较次数上进行优化,但移动次数未改变,复杂度仍然是O(N^2)

2-路插入排序

表插入排序

希尔SHELL排序

int shell_sort(int *ary,int size)
{
int op=0;
int temp;
int incs[5]={1,5,19,41,109};
for(int t=4;t>=0;t--)
{
int h=incs[t];
for(int i=h;i<size;i++)
{
temp=ary[i];
for(j=i;j>0&&temp<ary[j-h];j=j-h)
{
ary[j]=ary[j-h];
op++;
}
ary[j]=temp;
op++;
}
}
return op;
]

SHELL排序是不稳定的排序,

通常情况下性能优于插入排序

快速排序

// 从左遍历版本,以结尾为主元,返回的轴位置为i+1,因为这里i是从-1开始移动的
int partition(int *ary,int left,int right)
{
int i=left-1;
int j=left;
int pivot=ary[right];
for(;j<right;j++)
{
if(ary[j]<=pivot)
{
i++;
swap(i,j);
}
}
swap(i+1,right);
return i+1;
}
void quick_sort(int *ary,int left,int right)
{
if(left<right)
{
int index=partition(ary,left,right);
quick_sort(ary,left,index-1);
quick_sort(ary,index+1,right);
}
}

// HOARE版本,可以任意位置为主元,这里返回的轴位置是j+1,最后j在i前,可能j+1=i也可能j+2=i
void quicksort(int *ary,int left,int right)
{
if(left<right){
int i=left,j=right;
int pivot=ary[left];
while(i<=j){
while(ary[j]>pivot)j--;
while(ary[i]<pivot)i++;

if(i<=j){
int temp=ary[i];
ary[i]=ary[j];
ary[j]=temp;
i++;j--;
}
}
if(left<j)
quicksort(ary,left,j);
if(i<right)
quicksort(ary,i,right);
}
}
// 替换版本,非交换版本,这里返回的轴位置是i
void quicksort(int *ary,int left,int right)
{
if(left<right){
int i=left;
int j=right;
int key=ary[i];
while(i<j)
{
while(i<j&&ary[j]>=key)j--;
ary[i]=ary[j];
while(i<j&&ary[i]<=key)i++;
ary[j]=ary[i];
}
ary[i]=key;
if(left<i-1)
quicksort(ary,left,i-1);// 这里可以用i-1,因为ary[i]已经在有序的位置上了
if(i+1<right)
quicksort(ary,i+1,right);
}

}


快速排序基于分治法的排序算法,

时间复杂度为O(n*logn),基本有序的情况下(也就是最坏情况下)是O(n^2),但通常情况下性能是最佳的,常数因子较小

不稳定排序

为了避免基本有序的情况,通过引入随机化技术,使得在等概率的情况下获得期望性能,所作的操作只需要随机一个位置与每次作pivot的位置元素交换;

另外一种改进随机是三数取中划分,随机三个位置,然后取中位数为pivot

堆排序

void swap(int *a,int *b)
{
int temp=*a;
*a=*b;
*b=temp;
}
// 调整堆  siftdown
void heapify(int *ary,int i,int size)
{
int left,right,max;
while(i<size)
{
left=2*i+1;
right=left+1;
max=i;
if(left<size&&ary[i]<ary[left])
i=left;
if(right<size&&ary[i]<ary[right])
i=right;
if(i!=max)
swap(&ary[i],&ary[max]);
else
break;

}
}
// 建堆 siftup
void buildHeap(int *ary,int size)
{
for(int i=(size-2)/2;i>=0;i--)// 利用siftdown实现siftup,FOR 每个结点的父母结点 TO 根结点
{
heapify(ary,i,size);
}
}

// 堆排序过程 ,先建堆,然后交换顶点与末尾,交换一次再调整一次,当前代码是大根堆,所以最后顺序是从小到大
void heapSort(int *ary,int size)
{
buildHeap(ary,size);
for(int i=size-1;i>0;i--)
{
swap(&ary[0],&ary[i]);
heapify(ary,0,i);
}
}

堆排序的前身:树形选择排序,又称锦标赛排序(类似比赛分组树结构)

堆的定义是:一个完全二叉树,每个结点大于等于其两个孩子结点(大根堆)

堆排序的过程,首先建堆,然后将堆顶与末尾元素交换,紧接着调整堆使其继续保持定义,循环交换n次,形成最终有序排列

时间复杂度O(n*logn),其中调整堆的复杂度为O(logn),排序O(n),不稳定排序

堆排序的应用:

优先队列

归并排序

// 归并排序,不过new和delete写在子函数中,效率可能不高,可以直接在排序前申请O(n)的空间
void mergeArray(int *ary,int left,int mid,int right,int *temp)
{
int i=left,j=mid+1,k=0;
int temp[]=new int[right-left+1];
while(i<mid&&j<right)
{
if(ary[i]<ary[j])
temp[k++]=ary[i++];
else
temp[k++]=ary[j++];
}
while(i<=mid)
temp[k++]=ary[i++];
while(j<=right)
temp[k++]=ary[j++];
// 复制回原数组
for(int i=0;i<k;i++)
{
ary[left+i]=temp[i];
}
// 考虑是否删除额外空间
delete[] temp;
}
void mergeSort(int *ary,int left,int right,int *temp)
{
int mid=((right-left)>>1)+left;
if(left<right)
{
mergeSort(ary,left,mid);//注意这里不是 mid-1,之前写错了
mergeSort(ary,mid+1,right);
mergeArray(ary,left,mid,right,temp);
}
}


归并排序的时间复杂度最好最坏下均是O(n*logn),但空间复杂度是O(n)

是稳定排序

利用递归树可以清晰地分析归并排序的过程
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: