算法---基数排序

基数排序

一、算法描述:

对于排序问题,大部分的方法是基于比较大小然后交换位置。基数排序就不是基于比较的。它将整形10进制按每位拆分,然后从低位到高位依次比较各个位。主要分为两个过程:

(1)分配,先从个位开始,根据位值(0-9)分别放到0~9号桶中(比如53,个位为3,则放入3号桶中)

(2)收集,再将放置在0~9号桶中的数据按顺序放到数组中

重复(1)(2)过程,从个位到最高位(比如32位无符号整形最大数4294967296,最高位10位)。最后一次收集之后,序列有序。

 

 

二、分析:

看完算法描述,可能还有些疑惑。这样怎么就能排好呢?

依然拿整数举例,整数的比较是先看位数,位数多的值较大。相同位置上,数字大的较大。可以说,整数比较是高位优先的。这里基数排序需要低位优先,现将按低位,再按高位。细心的人,也许发现了。在第一次收集之后,看最低位,是有序的。在第二次收集之后,倒数第二位是有序的,而且在倒数第二位相同的情况下,它的下一位的数字也是有序的。这样依次类推,最终整个序列中,最高位有序,相同位置相同数字时,下一位有序。这样整个就是按照整数大小比较的规则形成有序序列了。

 

这样看来,

时间复杂度  O(d*(n + r))

:数字的位数

:元素个数

:数字的取值范围,如十进制数字,r= 10,十六进制的话,r= 16

 

空间复杂度

O(n + r)

这里需要桶的个数r,以及每个元素的指针n或者元素本身n



从这里的分析也可以看出,基数排序是有一些限制的。首先,需要知道待排序列中数字位数的最大值。其次,需要知道每一位的数字的取值范围。只有满足这两个条件才可以进行基数排序。


排扑克牌时,可以使用。花色权重 >  数字,而且相同花色,数字越大,牌越大。


三、代码:

 

 

 

给出两种数据结构的算法,第一种是最一般的使用链表作为桶,需要头指针和尾指针,每个元素都加入到链表中,再从链表里返回到序列中。

 

 

typedef struct JPoint
{
    int value;
    JPoint *pr;
} JPoint;

typedef struct JBarrel
{
    ///基数排序中,桶用链表队列实现
    JPoint *head;
    JPoint *rear;
} JBarrel;

void Initial_int(int *a,int n,int range)
{
    int i;
    srand( (unsigned)time( NULL ) );
    for(i=0; i<n; i++)
        a[i]=rand()%range+1;
}

void JishuSort_allocate(int *a,int n,JBarrel *B,int t)
{
    int ten=1,i,temp;
    for(i=0; i<t; i++)
        ten*=10;
    for(i=0; i<n; i++)
    {
        temp=(a[i]/ten)%10;
        JPoint *px=new JPoint;
        px->value=a[i];
        px->pr=NULL;
        if(B[temp].head)
        {
            B[temp].rear->pr=px;
            B[temp].rear=px;
        }
        else
        {
            B[temp].head=B[temp].rear=px;
        }
    }
}

void JishuSort_collect(int *a,int n,JBarrel *B,int nB)
{
    int i,j;
    for(i=0,j=0;i<nB&&j<n;i++)
    {
        if(!B[i].head)  continue;   ///B[i]为空队列
        if(B[i].head == B[i].rear)  ///队列只有一个元素
        {
            a[j++]=B[i].head->value;
            delete B[i].head;
        }
        else
        {
            JPoint *p=B[i].head;
            while(p)
            {
                a[j++]=p->value;
                JPoint *tem=p;
                p=p->pr;
                delete tem;
            }
        }
    }



}

void JishuSort(int *a,int n,int k)
{
    void print(int *a,int n);
    int BarrelNUM=10;
    JBarrel Barrel[BarrelNUM];
    int i,j;

    for(i=0; i<k; i++)
    {
        for(j=0; j<10; j++)
            Barrel[j].head=Barrel[j].rear=NULL;
        JishuSort_allocate(a,n,Barrel,i);
        JishuSort_collect(a,n,Barrel,BarrelNUM);
        //print(a,n);
    }
}

<pre name="code" class="cpp">void print_Jint(Jint *a,int n)
{
    int i=a[0].next;
    while(i != 0)
    {
        cout<<a[i].value<<"  ";
        i=a[i].next;
    }
    cout<<endl;
}

int Weishu_cnt(int a)
{
    /// 计算整个序列最大值的数字位数。
    int cnt=1;
    while(0 !=a/10 )
    {
        cnt++;
        a=a/10;
    }
    return cnt;
}
 

 

第二种是使用静态链表,忘记的同学可以自行复习。使用静态链表的好处是可以减少赋值操作,因为第一种链表的数据结构中,分配时需要形成一个链表节点,这里赋值一次,收集时,赋值返回时有需要一次。如果使用静态链表,只需要操作数组的第二维(也就是下一个数的地址,类似于指针)的值,进行一些逻辑运算。可以减少不少操作时间,提高效率,节约空间。不过复杂度的量级不变,只不过是前面的系数减小。

静态链表可以看成,有物理顺序和逻辑顺序的序列。

这里Basket结构体用来记录桶中队首和队尾元素在序列中的位置。因为这里的桶,也是符合先进先出,所以是队列。


typedef struct Jint
{
    int value;
    int next;
} Jint,*JintP;

struct Basket
{
    int head;
    int rear;
};

void Initial_Jint(Jint *a,int n,int range)
{
    /// 随机产生n个元素,使用静态链表结构
    int i;
    srand( (unsigned)time( NULL ) );
    for(i=1; i<n; i++)
    {
        a[i].value=rand()%range+1;
        a[i].next=i + 1;
    }
    a[0].next=1;
    a[n-1].next = 0;
}



void JishuSort_allocate_G(Jint *a,int n,struct Basket *b,int k)
{
    int ten=1,i,temp;
    for(i=0; i<k; ++i)
        ten*=10;
    if(1 == ten)
    {
<span style="white-space:pre">	</span>/// 第一次分配时,因为元素逻辑顺序和物理顺序一致,所以直接遍历,减少一点儿时间吧。
        for(i = 1; i < n; ++i)
        {
            temp = (a[i].value / ten) % 10;
            if(-1 == b[temp].head)
            {
<span style="white-space:pre">		</span>/// 桶里没有元素。首插。下同。
                b[temp].head = b[temp].rear = i;
            }
            else
            {
                int t = b[temp].rear;
                a[t].next = i;
                b[temp].rear = i;
            }
        }
    }
    else
    {
<span style="white-space:pre">	</span>/// 这里,只能老老实实的按照逻辑顺序来遍历。
        for(i = a[0].next; i > 0; )
        {
            temp=(a[i].value/ten)%10;
            if(-1 == b[temp].head)
            {
                b[temp].head = b[temp].rear =i;
            }
            else
            {
                int t=b[temp].rear;
                a[t].next = i;
                b[temp].rear=i;
            }
            i = a[i].next;
        }
    }
}



void JishuSort_collect_G(Jint *a,int n,struct Basket *b)
{
    void print_Jint(Jint *a,int n);
    int i;
    int Blast=0;

    for(i = 0; i<10; ++i)
    {
<span style="white-space:pre">	</span>/// 分配后桶里没有元素,则跳过。
        if(-1 == b[i].head)
            continue;
        else
        {
<span style="white-space:pre">	</span>    /// 这个桶中的队头接到上一个桶的队尾,并记录当前桶队尾元素。Blast。
            a[Blast].next=b[i].head;
            Blast=b[i].rear;
        }

    }
    a[Blast].next=0;
    print_Jint(a,n);
}



void JishuSort_G(Jint *a,int n,int k)
{
    void print_Jint(Jint *a,int n);
    int i,j;
    struct Basket Barrel[10];
    for(i=0; i<k; i++)
    {
        for(j=0; j<10; ++j) {Barrel[j].head = Barrel[j].rear = -1;}
        JishuSort_allocate_G(a,n,Barrel,i);
        JishuSort_collect_G(a,n,Barrel);

        //
    }
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值