• 【算法导论】第8章线性时间排序_计数排序、基数排序、桶排序




    1、问题引入

      之前的堆排序、快速排序都是基于输入元素间的比较,这类排序叫做比较排序,对于n个元素的比较排序可以证明其在最坏情况下都要用O(nlgn)次比较来进行排序,本节中将讨论三种以线性时间运行的算法:计数排序、基数排序、桶排序,这些算法都用非比较的一些操作来确定排序顺序。



    2、算法讨论

    2.1 计数排序

      计数排序假设n个输入元素中的每一个都是介于0到k之间的整数(k为某个整数),其基本思想是:对于每一个输入元素x,确定出小于x的元素个数。有了这一信息,就可以吧x直接放到它在最终输出数组中的位置上,例如,如果有17个元素小于x,则x就属于第18个输出位置。

      在计数排序中,假定输入数组是A[1...n],length[A]=n.另外还需要两个数组:存放排序结果的B[1...n],以及提供临时存储区的C[0...k]。

      具体实现代码如下:

    View Code
     1 #include<stdio.h>
     2 #include<malloc.h>
     3 void countingsort(int a[],int b[],int n,int k);
     4 void countingsort(int a[],int b[],int n,int k)
     5 {
     6     int i;
     7     int *c=(int*)malloc(sizeof(int)*(k+1));
     8     //利用三个for循环实现c[i]的初始化
     9     for(i=0;i<=k;i++)
    10         c[i]=0;
    11     for(i=0;i<n;i++)
    12         c[a[i]]+=1;
    13     for(i=1;i<=k;i++)
    14         c[i]=c[i]+c[i-1];
    15     //for(i=0;i<=k;i++)
    16     //    printf("%d   ",c[i]);
    17 
    18     for(int j=n-1;j>=0;j--)//注意a[],b[]数组下标开始顺序
    19     {
    20         b[c[a[j]]-1]=a[j];
    21         c[a[j]]-=1;
    22     }
    23 }
    24 void main()
    25 {
    26     int k=5,n=8;
    27     int a[8]={2,5,3,0,2,3,0,3};
    28     int b[8];
    29     
    30     printf("before sort:\n");
    31     for(int j=0;j<8;j++)
    32         printf("%d  ",a[j]);
    33     countingsort(a,b,n,k);
    34     printf("after sort:\n");
    35     for(int i=0;i<8;i++)
    36         printf("%d  ",b[i]);
    37 }


    2.2 基数排序

      基数排序是借助“分配”和“收集”两种操作对单逻辑关键字进行排序的一种内部排序方法,例如关键字是数值,且其值都在0《k《999之间,则可以把每一个十进制数字看成一个关键字,即认为k由3个关键字(k1,k2,k3)组成,其中k1为个位数,k2为十位数,k3为百位数,例如三位的数值排序,可以设radix(=10)个队列,d=3;按照LSD进行排序,只要从最低位关键字起,按照关键字的不同值将序列中记录“分配”到RADIX队列后再“收集”,如此重复d次,这种方法就是基数排序。其中“基”是指radix的取值。

      本次实现中采用链队列:

      基本算法过程如下:

      具体算法如上图所示,下面给出具体实现代码:

    View Code
     1 #include"queue.h"
     2 #include<stdio.h>
     3 #define keysize 3//关键字位数为3
     4 #define radix 10//基数为10
     5 void radixsort(int r[],int n);
     6 void distribute(int r[],linkqueue b[],int j,int n);
     7 void collect(int r[],linkqueue b[]);
     8 void radixsort(int r[],int n)
     9 {
    10     int i;
    11     linkqueue b[radix];
    12     for(i=0;i<radix;i++)
    13         initqueue(b[i]);
    14     for(i=keysize-1;i>=0;i--)
    15     {
    16         distribute(r,b,i,n);
    17         collect(r,b);
    18     }
    19 }
    20 void distribute(int r[],linkqueue b[],int j,int n)
    21 {
    22     int i,k,t;
    23     j=keysize-j;
    24     for(i=0;i<n;i++)
    25     {
    26         k=r[i];
    27         for(t=1;t<j;t++)k=k/10;
    28         k=k%10;
    29         enqueue(b[k],r[i]);
    30     }
    31 }
    32 void collect(int r[],linkqueue b[])
    33 {
    34     int e,i=0,j;
    35     for(j=0;j<radix;j++)
    36         while(!queueempty(b[j]))
    37         {
    38             dequeue(b[j],e);
    39             r[i++]=e;
    40         }
    41 }
    42 void main()
    43 {
    44     int i,n=7;
    45     int r[7]={329,457,657,839,436,720,355};
    46     radixsort(r,n);
    47     for(i=0;i<7;i++)
    48         printf("%d  ",r[i]);
    49 }


    2.3 桶排序

      当桶排序的输入符合均匀分布时,即可以以线性期望时间运行。与计数排序类似,桶排序也对输入作了某种假设,因而运行得很快。具体来说,计数排序假设输入是由一个小范围内的整数构成,而桶排序则假设输入由一个随机过程产生,该过程将元素均匀而独立的分布在区间[0,1)上。

      桶排序的思想就死把区间[0,1)划分成n个相同大小的子区间,或称桶。然后,将n个输入数分布到各个桶中去。因为输入数均匀且独立分布在[0,1)上,所以,一般不会有很多数落在一个桶中的情况。为了得到结果,先对各个桶中的数进行排序,然后按次序把各桶中的元素列出来即可。

      在桶排序算法的代码中,假设输入的是一个含n个元素的数组A,且每个元素满足0《A[i]<1.另外,还需要一个辅助数组B[0..n-1]来存放链表(桶),并且可以用某种机制来维护这些表。

      具体的实现代如下:

    View Code
     1 #include<stdio.h>
     2 #include<stdlib.h>
     3 struct keyNum
     4 {
     5     double key;//关键字
     6     struct keyNum *next;
     7 };
     8 struct keyNum*hash[100];
     9 struct keyNum* insertHash(struct keyNum*,double);//关键字插入链表
    10 void print(struct keyNum*);//打印链表
    11 void main()
    12 {
    13     int i,k,n=10,num=10;//n表示划分的桶的个数,num表示输入元素个数。
    14     double a[10]={0.78, 0.17, 0.39, 0.26, 0.72, 0.94, 0.21, 0.12, 0.23, 0.68 };
    15     struct keyNum *head=NULL;
    16     for(i=0;i<num;i++)
    17         hash[i]=NULL;
    18 
    19     for(i=0;i<num;i++)
    20     {
    21         k=int(a[i]*10);
    22         hash[k]=insertHash(hash[k],a[i]);//将关键字k插入到哈希值为m的链表中
    23     }
    24     printf("排序前为:\n");
    25     for(i=0;i<num;i++)
    26         printf("  %f",a[i]);
    27     printf("\n");
    28 
    29     printf("排序结果为:\n");
    30     for(i=0;i<num;i++)
    31         print(hash[i]);    
    32 }
    33 struct keyNum * insertHash(struct keyNum*head,double m)
    34 {
    35     struct keyNum *p0,*p1,*p2,*temp;
    36     temp=(struct keyNum*)malloc(sizeof(struct keyNum));
    37     temp->key=m;
    38     p1=head;
    39     p0=temp;//要插入的节点(值为m);
    40     if(head==NULL)//1,原来的链表为空,插入到head后
    41     {
    42         head=p0;
    43         p0->next=NULL;
    44     }
    45     else//原来的链表不为空
    46     {
    47         while((p0->key>p1->key)&&(p1->next!=NULL))//移动到适当位置
    48         {
    49             p2=p1;
    50             p1=p1->next;
    51         }
    52         if(p0->key<=p1->key)
    53         {
    54             if(head==p1)head=p0;//2,插入到第一个节点之前
    55             else p2->next=p0;//3,插入到p2指向的节点之后
    56             p0->next=p1;
    57         }
    58         else//4,插入到结尾处
    59         {
    60             p1->next=p0;
    61             p0->next=NULL;
    62         }
    63     }
    64     return(head);
    65 }
    66 
    67 void print(struct keyNum*head)//打印链表head
    68 {
    69     struct keyNum*p;
    70     p=head;
    71     if(head!=NULL)
    72     {
    73         do
    74         {
    75             printf("  %f",p->key);
    76             p=p->next;
    77         }while(p!=NULL);
    78     }
    79     else{}
    80 }

    3、小结
      具体的代码实现参见算法就可以很容易完成,不过没有给出具体的时间复杂度分析。。

    4、参考文献:

    (1)算法导论

    (2)数据结构

    (3)http://baike.baidu.com/view/1170573.htm

  • 相关阅读:
    POJ 1953 World Cup Noise
    POJ 1995 Raising Modulo Numbers (快速幂取余)
    poj 1256 Anagram
    POJ 1218 THE DRUNK JAILER
    POJ 1316 Self Numbers
    POJ 1663 Number Steps
    POJ 1664 放苹果
    如何查看DIV被设置什么CSS样式
    独行DIV自适应宽度布局CSS实例与扩大应用范围
    python 从入门到精通教程一:[1]Hello,world!
  • 原文地址:https://www.cnblogs.com/lpshou/p/2553370.html
Copyright © 2020-2023  润新知