关于各种排序算法的代码分析

选择排序是在第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]);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值