算法 基于比较的排序

来源:互联网 发布:卡尔曼滤波算法视频 编辑:程序博客网 时间:2024/05/21 07:11

插入排序

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=ivoid 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);     }}// 替换版本,非交换版本,这里返回的轴位置是ivoid 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; }// 调整堆  siftdownvoid 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]);elsebreak;}}// 建堆 siftupvoid 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++];elsetemp[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)

是稳定排序

利用递归树可以清晰地分析归并排序的过程


0 0