部分类型排序(梳排序,归并排序,快速排序,基数排序)的简单梳理

常见的排序可分为比较排序及其非比较排序,其中比较排序较常用的有冒泡排序,归并排序,插入排序,选择排序,快速排序,梳排序等等;非比较排序有桶排序,基数排序等等。我们将介绍其中的梳排序,归并排序,快速排序,基数排序。网上关于这些实现排序的方法可能有多种,这里每一种排序仅提供其中一种实现方法作为参考。

第一种:比较排序

梳排序:冒泡排序的优化版。

原理:交换的范围由冒泡的与旁边的数比较并,变成了与隔一段距离的数比较并交换,并每过一个循环减少这个距离,当距离为1时,就相当于冒泡,但是在退化成冒泡前,我们已经对数据进行了多次比较排序,所以进行冒泡的步骤就十分少了,变形加速了冒泡。所以速度>=冒泡

#include<stdio.h>
#define N 1000
int main()
{
int n;//要处理数据的大小
int i;//计数器
int j;//距离
int a[N];//存储数据的数组
    double k=1.3;//距离递减值,可以是其他,但1.3测试的效果较好
    scanf("%d",&n);
    for(i=0;i<n;i++){scanf("%d",&a[i]);}
    j=n-1;
    i=0;
    int flag=1;//判断是否有交换,如有交换循环还要继续,否则(部分)排序结束
    //两个条件同时满足时,才能认为数据中每一个数都被比较过并处理过
while(j>1||flag==1){
    j=(j/k>1)?(j/k):1;//每循环一次让 距离/递减值,减少距离,直到为1,则退化成冒泡
    flag=0;
i=0;
//下面进行冒泡
    while(i+j<n){
if(a[i]<a[i+j]){
        int temp=a[i];
        a[i]=a[i+j];
        a[i+j]=temp;
        flag=1;
    }//大到小排序,交换
     i++;
    }
    }
    for(i=0;i<n;i++){printf("%d ",a[i]);}//输出
    return 0;
}

归并排序:稳定的高速算法

原理:借助分治法,将要处理的数据分半,再分半,直到只剩两个数的时候不能再分,开始对分半到最后的那一堆数据(子数据)分别进行排序
步骤:

1. 每个子数据中的数比较大小并换位
2. 递归到上一步,和旁边的(排好序的)子数据进行合并
(合并步骤:)

  1. 临时建立一个空数组Z[ n ]
  2. 比较两个子数据a[ i ],b[ j ]首元素大小,大的(如果是a[0])则放进Z[0]
  3. 然后比较a[1]和b[0],大的放进Z[1](每放进一次,Z和放进元素的数组下标+1)
  4. 重复
  5. 如果两个子数据长度不等,把剩下的元素不按顺序直接放进Z
  6. 回到2,直到全部合并。

因为该方法无论数据大小只会影响分半速度,所以该方法比较稳定,无论如何速度>冒泡。
但不一定大于梳排序。

代码描述可能更容易理解:

#include<stdio.h>
#define N 1000
void Mergesort(int a[],int L,int R);
int main()
{
    int n,i;
    int a[N];
    scanf("%d",&n);
    for(i=0;i<n;i++){scanf("%d",&a[i]);}
    Mergesort(a,0,n);
    for(i=0;i<n;i++){printf("%d ",a[i]);}
    return 0;
}

void Mergesort(int a[],int L,int R)
{
if(L>R-2){return;}//当数据只剩下1个的时候,结束,回到上一层递归
int middle=(L+R)/2;//中间值,负责分半

Mergesort(a,L,middle);
Mergesort(a,middle,R);//把数据分开两半

int x=L;//子数据1的下标
int y=middle;//子数据1旁边的子数据2的下标
int t=L;//空白数组的下标
int turn[N]={0};//空白数组

while(x<middle||y<R)//对a[L]……a[middle-1],和对a[middle]…….a[R-1]数据进行合并
{
    if(y>=R||(x<middle&&(a[x]>=a[y]))){turn[t++]=a[x++];}//把大的先放进去turn[ ],达到从大到小排序的目的。忘了x<middle时循环可能会越过middle边界。如果子数据2已经越了界,则把子数据1全部放入turn[ ]中
    else{turn[t++]=a[y++];}
}

for(int i=L;i<R;i++){
a[i]=turn[i];
}//把turn[ ]中排好的数据放回原来的数组

}

这种递归算法虽然会有空间上的开销(空白数组的开设),和时间上的开销(递归,调用函数)。但是比较好理解,容易易写和相对不容易出错,有兴趣可以上网查一下非递归版本

快速排序:不稳定的更高速算法

原理:也同样用到分治法,将将数据分成两部分再分。不同的是对于每一组数据,将选取其中一个数作为基准,然后分别将大于基准数的元素置于基准数左边,小于则放于右边,最后将基准数两边的数据分开,分别继续刚才的步骤,直到不能再分,但此时,全部数据已经完成排序。

不稳定性:假如我们要将一组数据按大到小排序,而这组数据原来是由小到大排序的话,这时快速排序效率等于冒泡排序,甚至还慢了一点(函数调用),但大部分情况下,快速排序效率高于其他排序,广泛运用于各种函数库。

#include<stdio.h>
#define N 1000
void quicksort(int a[],int L,int R);
void Swap(int *a,int*b);
int main()
{
    int n,i;
    int a[N];
    scanf("%d",&n);
    for(i=0;i<n;i++){scanf("%d",&a[i]);}
    
    quicksort(a,0,n);
    
    for(i=0;i<n;i++){printf("%d ",a[i]);}
    return 0;
}
void quicksort(int a[],int L,int R){
if(L==R-1){return;}
int x=L+1,y=R-1;
while(x!=y){
    while(a[y]<a[L]&&y>x){y--;}
    while(a[x]>a[L]&&x<y){x++;}
    if(x<y){Swap(&a[x],&a[y]);}
}

if(a[L]<a[y])Swap(&a[L],&a[y]);

quicksort(a,L,x);
quicksort(a,x,R);
}

void Swap(int *a,int*b){
int temp=*a;
*a=*b;
*b=temp;
return;
}

第二种:非比较排序

基数排序:处理小数据的有效排序

输入数据,边输入边边比较各个元素的位数,取出最大位数
进入排序的函数中,从1位(个位数)开始,将元素按照个位数(此时个位数为排序基数)大小的顺序临时放在一个空白数组中,大的放前面,小的放后面。放完后,对2位(十位,此时十位数为排序基数)开始,重复刚才的步骤,如果小于2位,则优先放在最前面。
重复刚才步骤,直到位数过了最大的位数。排序结束了。

该方法通常用于处理小的自然数数据,稳定,比冒泡快。

#include<stdio.h>
#define N 1000
int main()
{
    int n,i;
    int a[N];
    scanf("%d",&n);
    int all=-1;
    
    for(i=0;i<n;i++)
    {
    int s=0;
    scanf("%d",&a[i]);
    int x=a[i];
    while(x){s++;x/=10;}
    if(s>all){all=s;}//找到最大位数
    }
    
    int lo=1;//排序从1(个位)开始
    while(lo<=all){
    int k=1;
    int Lo=lo;
    
    while(--Lo){
        k=k*10;//用来处理元素的工具
    }
    
    int A[n+3]={0};
    int j,c=0;
    
    for(i=9;i>=0;i--){
        for(j=0;j<n;j++){
            if((a[j]/k)%10==i)//对每一个元素,先/k使其最后一位变成我们现在进行排序的位数,如我们现在要排2(十位),使123/10=12,12%10=2,2即为123的十位数。
        {
                A[c++]=a[j];//基数大元素的先放进空白数组。
        }
        }
    }
 
    for(i=0;i<n;i++){a[i]=A[i];}//空白数组中的数据回到原数组
    lo++;
    }
    
    
    for(i=0;i<n;i++){printf("%d ",a[i]);}
    
    return 0;
}

所学排序的简单梳理就到这里,欢迎大家提出建议。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值