选择排序是在第i趟排序时选出第i小的放在为排序的元素之首,比如第一次选出最小的放在数组第一位a[0],第二次选出除了a[0]之外最小的放在第二位a[1],以此类推。在算法上表现为需要进行n-1次选择,每次记下flag=i,用a[flag]与a[i+1]到a[n-1]依次比较,如果被比较的值较小,则将flag更新,最后如果flag与i不等,则交换两个值,下一次排序从i+1开始即可。
//选择排序
void selectsort(int *a,int n){
int i,j,k,temp;
for(i=0;i<n-1;i++){
k=i;
for(j=i+1;j<n;j++){
if(a[j]<a[k])k=j;
}
if(k!=i){
temp=a[i];a[i]=a[k];a[k]=temp;
}
}
}
直接插入相当于将数组从第一个数开始插入已经排好序的数组中,第一个数相当于已经有序插入,所以排序从第二个数到第n个数,进行n-1次循环。每次第i个数与前面i-1个数进行比较,比它大就往后移,直到该数比它小,则在后一位插入要排序的数。折半插入类似,只不过将寻找插入位置的方法变成了二分查找。
//直接插入
void insertsort1(int a[],int n){
int i,j,temp;
for(i=1;i<n;i++){
if(a[i]<a[i-1]){
temp=a[i];
for(j=i-1;j>=0;j--){
if(a[j]>temp)a[j+1]=a[j];
else break;
}
a[j+1]=temp;
}
}
}
//折半插入
void insertsort2(int a[],int n){
int i,j,temp,low,high;
for(i=1;i<n;i++){
temp=a[i];
low=0;high=i-1;
while(low<=high){
j=(low+high)/2;
if(a[j]>temp)high=j-1;
else low=j+1;
}
for(j=i-1;j>=high+1;j--)a[j+1]=a[j];
a[j+1]=temp;
}
}
冒泡在这里改进了一下,每比较一次判断一下交换次数,如果没有发生交换,则后面的所有数已经有序,直接返回即可。
//冒泡
void bubblesort(int a[],int n){
int i,j,temp,count;
for(i=0;i<n;i++){
count=0;
for(j=0;j<n-i-1;j++){
if(a[j]>a[j+1]){
temp=a[j]; a[j]=a[j+1]; a[j+1]=temp; count++;
}
}
if(count==0) return;
}
}
归并的思想是将待排序元素不断划分,然后自底向上合并元素,最底层是两个一组,组内排序,上一层将四个元素合并,不断向上。划分数组时一般使划分的规模大致相等,合并这里代码的思想是先将合并的两个数组拷贝到新的数组里,然后比较将较小的放入原数组。
//归并
void merge(int *a,int p,int q,int s){
int i,j,k,n1,n2;
n1=q-p+1;
n2=s-q;
int *l=new int[n1];
int *r=new int[n2];
for(i=0,k=p;i<n1;i++,k++) l[i]=a[k];
for(i=0,k=q+1;i<n2;i++,k++) r[i]=a[k];
for(k=p,i=0,j=0;i<n1&&j<n2;k++){
if(l[i]>=r[j])a[k]=r[j++];
else a[k]=l[i++];
}
if(i<n1)
for(i;i<n1;i++,k++)a[k]=l[i];
if(j<n2)
for(j;j<n2;j++,k++)a[k]=r[j];
delete []l;
delete []r;
}
void mergesort(int *a,int p,int q){
if(p<q){
int mid=(p+q)/2;
mergesort(a,p,mid);
mergesort(a,mid+1,q);
merge(a,p,mid,q);
}
}
快排与归并是相反的过程,是先找到一个元素的位置,然后对两部分分别执行快排过程。找到元素的位置可以先将该元素记录下来作为基准,比它大的往后放,比它小的往前放。一般是将要排序的第一位作为基准,设一个low与high变量,从最后一位开始找,如果比它大就不管,比它小就把该元素与low交换,然后从low+1开始往后找,最后high<=low时结束。当数组元素基本有序时,快排会退化,解决办法是随机设置基准,不要设置为第一位。
//快排
int partition(int a[],int l,int h){
int temp=a[l];
while(l<h){
while(l<h&&temp<=a[h]) h--;
if(l<h){
a[l]=a[h]; l++;}
while(l<h&&a[l]<=temp) l++;
if(l<h){
a[h]=a[l]; h--;}
}
a[h]=temp;
return h;
}
void quicksort(int a[],int l,int h){
int t;
if(l<h){
t=partition(a,l,h);
quicksort(a,l,t-1);
quicksort(a,t+1,h);
}
}
希尔排序也是分组排序,但与归并不同的是希尔是以步长h分组,即间隔h的数为一组排序,组内为插入排序,每次减小步长,直到步长为1,步长的设置要合理,最好不要有公约数。
//希尔排序
void shellsort(int a[],int n){
int i,j,temp,h;
for(h=n/2;h>0;h=h/2){
for(i=h;i<n;i++){
temp=a[i];
for(j=i-h;j>=0;j-=h){
if(a[j]>temp) a[j+h]=a[j];
else break;
}
a[j+h]=temp;
}
}
}
堆排序是将数组看作一棵完全二叉树,完全二叉树的特点是结点i的左孩子为2i,右孩子为2i+1,且最后一个有孩子的节点为n/2-1,所以从该结点开始堆排初始化,选出孩子中的最大值,与父节点比较,将父节点置为最大的值,然后从改变了的结点继续往下找。这样根节点就是最大值,将其与最后一个值交换,对剩下的继续堆排。
//堆排序
void adjustmaxheap(int a[],int s,int len){
int temp,i;
temp=a[s];
for(i=s*2;i<len;i*=2){
if(i<len&&a[i]<a[i+1]) i++;
if(a[i]>temp) {a[s]=a[i]; s=i;}
else break;
}
a[s]=temp;
}
void maxheapsort(int a[],int n){
int i,temp;
for(i=n/2-1;i>=0;i--)
adjustmaxheap(a,i,n-1);
for(i=n-1;i>=0;i--){
temp=a[0];
a[0]=a[i];
a[i]=temp;
adjustmaxheap(a,0,i-1);
}
}
void main(){
int i,a[]={4,2,8,6,0,7,3,9,1,5};
int len=sizeof(a)/sizeof(a[0]);
maxheapsort(a,len);
for(i=0;i<len;i++)printf("%d\n",a[i]);
}