排序算法4

天讲两种比较诡异的排序算法,之所以诡异是因为这两种排序算法与之前介绍的几种不太一样(见排序算法前三篇)。之前的排序算法都是基于元素比较来得到的,它们中间,像堆排序,快排等都有着比较理想的时间复杂度下界O(nlgn)。那么有没有更快的排序算法?有!下面的这两种排序算法的最好时间复杂度是O(n)。是不是很诱惑?一起来看看。

1)桶排序

桶排序实际上是先定义m个单元,每个单元满足一定的条件并按顺序排列,然后将所有的数据根据一种映射法则,分配到相应的单元内。然后对每个单元进行排序,最后汇总。说的很抽象,下面举个例子说明:假设现在要统计一个学院的期末考试成绩,共有1000个学生。每个学生的成绩是0-100分之间。那么如何对所有学生的分数进行排名?

so easy!我们定义一个指针数组arr[101],每个元素假设又指向一个容器,这个容器是什么数据结构我们先不管。我们暂且称为桶。并且规定arr[0]对应的桶存放所有分数为0的成绩,arr[1]对应的桶存放分数为1的成绩,依次类推,arr[100]存放分数为100的成绩。于是遍历查询这1000个学生的成绩,并依据分数投放到数据中对应的桶内。最后按下标序号依次输出每个桶内的输出数据,是不是就已经排好序了?ok,这就是桶排序的真相。如果桶足够大,它几乎是没有什么耗时的。当然这只是最简单的情况,不过不管怎么样,桶排序在保证一个桶内只有一个元素时,复杂度就是O(n)了,不过这要付出空间代价,因为你要使得桶足够大。另外,这个例子也说明了桶排序是有限制条件的,那就是学生的分数必须是在某一个明确的范围内,这里是[0,100]。这很好理解,如果分数是不确定的,那么就无法建立桶的序号与分数的关系了,那么你怎么能找到分数对应哪个桶呢?

桶排序的适用场景:元素的取值范围在一个比较小的范围内

桶排序一般要解决两个问题:1、上面桶的结构如何实现?你可以一次定义一个比较大的数组,省得麻烦,像上面那样,每个桶设置成1000大小的数据,就算所有学生的成绩全部是0分或100分,我们也不用担心桶被放满。但是这太浪费了,所以我们可以采用链表。即每个数组元素对应一个链表指针。

2、如何将数据映射到桶的序号上去?即你要知道你的这个数据该放到哪个桶内?

下面举个简单的例子来说明桶排序的实现。

例子:对数组 {2, 3, 1, 29, 70, 16, 34, 11, 10, 33, 79, 6, 46, 100, 25, 82}排序。

初步看出,元素取值范围为[0,100]。你可以像排成绩的例子中那样,定义一个100大小的指针数组,依次投放到各个桶,再按序输出就ok了。不过为了清晰的说明桶排序的两个问题,我们要这样搞:定义一个大小为10的数组,每个元素对应一个链式结构的桶。于是数组的第一个桶用来存放[0-10]的元素,第二个桶存放[11-20]的元素,依次类推,最后一个桶存放[91-100]的元素。那么我们要用到的映射准则就很明了了,一个数n对应到下标是n/10的桶,如果n是10的整数倍,下标要减1。上代码:

 1 int MapForBarrel(int n)
 2 {
 3     if(n%10 == 0)
 4         return (n == 0)?0:(n/10 - 1);
 5     else
 6         return n/10;
 7 }
 8 /***barrel sort************************************
 9 ***array-the array to be sorted
10 ***nsize-the size of array
11 ***ncount-the number of barrel
12 ***pmapfunc-the custom map function
13 **************************************************/
14 void BarrelSort(int array[], int nsize, int ncount, void* pmapfunc)
15 {
16     /**buile a barrel**/
17     typedef int (*pfunc)(int);//function pointer to map
18     typedef struct _node //list node struct
19     {
20         int data;
21         struct _node *pnext;
22     }node,*pnode;
23     pnode *barrel,ptemp;
24     int i,j,ntemp;
25     int index;
26     pfunc mapfunction;
27     pnode plisthead, plistlast, plisttemp;
28 
29     if(NULL == pmapfunc)
30         return;
31     mapfunction = (pfunc)pmapfunc;//assign the map function pointer
32     barrel = (pnode *)malloc(ncount*sizeof(pnode));//malloc for the barrel
33     if(NULL == barrel)
34         return;
35     for(i=0; i<ncount; i++)
36     {
37         barrel[i] = 0;//initialise the barrel
38     }
39 
40     /**begin to deal with all element in array***/
41     for(i=0; i<nsize; i++)
42     {
43         ntemp = array[i];
44         index = mapfunction(ntemp);
45         plisttemp = (pnode)malloc(sizeof(node));//malloc a temporary node 
46         plisttemp->data = ntemp;
47         plisttemp->pnext = NULL;
48         if(NULL == barrel[index])//the list is empty
49              barrel[index] = plisttemp;
50         else
51         {
52             plistlast = plisthead = barrel[index];
53             while(plisthead && plisthead->data < ntemp)
54             {
55                 plistlast = plisthead;
56                 plisthead = plisthead->pnext;
57             }
58             if(plisthead == barrel[index])//insert this node at the list begin
59             {
60                 barrel[index] = plisttemp;
61                 plisttemp->pnext = plisthead;
62             }
63             else//inset this node at the middle or end postion of the list
64             {
65                 plisttemp->pnext = plistlast->pnext;
66                 plistlast->pnext = plisttemp;
67             }
68         }
69     }
70     /***the barrel is sorted, than copy back to array***/
71     for(i=0,j=0; i<ncount; i++)
72     {
73         plisttemp = barrel[i];
74         while(plisttemp)
75         {
76             plistlast = plisttemp;
77             array[j++] = plisttemp->data;
78             plisttemp=plisttemp->pnext;
79             free(plistlast);//free the node;
80         }
81     }
82     /***release the barrel's memory***/
83     free(barrel);
84 }

所以,最后桶排序结束以后的数据存放形式是这样的:

 

2)bitmap排序

 这个排序算法就更加有趣了,简直就是非主流的思想。与桶排序类似,也是讲元素映射到相应的单元,但是它并不去比较元素,而是巧妙的借助单元序号达到自动排序的目的。还是很抽象,下面举个例子:对数组a{1,5,3,2,4}排序,我们先定义一个大小大于等于6的数组b(大了也没用),并初始化每个元素为0,为什么是6,等下说。然后遍历要排序的数组元素a,并将每个元素的值作为数组b的下标索引,并将该索引位置的b的值置为1,完了以后,遍历b数组,如果当前位置元素值为1,则输出其位置的值(注意不是位置元素的值),这样完了以后,输出的结果就是已经排好序的a数组了。不信你试试看。

a=1;   b={0,1,0,0,0,0};

a=5;   b={0,1,0,0,0,1};

a=3;   b={0,1,0,1,0,1};

a=2;   b={0,1,1,1,0,1};

a=4;   b={0,1,1,1,1,1};

然后遍历b,如果某一位为1,输出其下标,结果为:

1,2,3,4,5

是不是很神奇?之所以能排序,是因为我们实际上利用了数据和下标之间的一一对应关系,实现了一个巧妙的转换。所以要排序的数组元素必须各不相同,且新数组(我们成为bitmap数组)的大小不能小于排序元素的最大值。这就是上面为什么要至少定义一个不小于6个元素的数组b的原因。

弄清楚了bitmap排序的思想以后,接下来就是如何实现的问题了。其实bitmap排序实现的关键步骤就是如何将元素值对应的bitmap数组下标位置的值置1,有点绕口,慢慢理一下就好。非常简单,看代码:

 

 1 void BitMapSort(int array[], int narrsize, int bitarray[], int nbitarrsize)
 2 {
 3     int i,j;
 4     int index;
 5     for(i=0; i<nbitarrsize; i++)
 6         bitarray[i]  = 0;//初始化为0
 7     for(i=0; i<narrsize; i++)
 8     {
 9         index = array[i];//原数组的值,也是对应bitarray的下标
10         bitarray[index] = 1;//bitarray[index]置1;
11     }
12     for(i=0,j=0; i<nbitarrsize; i++)
13     {
14         //测试bitmap数组的各个位置的值是否为1
15         if(bitarray[i] == 1)
16             array[j++] = i;
17     }
18 }

 

从代码上可以看出,bitmap排序算法不需要比较,时间复杂度为O(n)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值