zoukankan      html  css  js  c++  java
  • 【算法导论】第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

  • 相关阅读:
    linux 定时任务
    fastcgi_param 详解
    MVC 中 注册不成功 或其他操作不成功 提示办法
    关于MVC 中EF调用存储过程
    关于Mvc的分页写法
    关于Linq翻译Inner join ,Left join (本文为转载)
    GridView自定义自增长的 序号 列
    asp.net操作GridView添删改查的两种方法 及 光棒效果
    Jquery编历数组
    在客户与服务器之间传递二进制结构
  • 原文地址:https://www.cnblogs.com/lpshou/p/2553370.html
Copyright © 2011-2022 走看看