7,桶排序、基数排序、希尔排序、计数排序、位图排序、其他排序

桶排序是将数组分散到有限的桶中,然后每个桶再分别排序,而每个桶的排序又可以使用其他排序方式进行排序,可以是桶排序也可以是其他排序。桶的大小可以随便定,如果桶的数量足够多就会变成我们后面介绍的计数排序,其实我们完全可以把桶固定在一个数量,根据数组的大小来确定,也可以自己定,比如3个或者5个7个等,桶的大小确定之后,下一步就需要把数组中的值一一存放到桶里,小的值就会放到前面的桶里,大的值就会放到后面的桶里,中间的值就会放到中间的桶里,然后再分别对每个桶进行单独排序,最后再把所有桶的数据都合并到一起就会得到排序好的数组,看代码

我们用一组数据来测试一下

看一下运行结果

结果完成正确,这就是所谓的桶排序,首先要找到他的最大值和最小值,然后计算桶的数量,找出最小值是因为存放的时候要让当前值减去最小值,否则当排序中有负数的时候存放到桶里会报异常,代码中也都有注释,这里就不在详细介绍。


基数排序的方式可以采用最低位优先LSD(Least sgnificant digital)法或最高位优先MSD(Most sgnificant digital)法,LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。我们这里使用LSD法,原理就是一个数组我们首先根据他的个位进行排序,然后在根据十位,百位……,这里最多排到多少位是根据他的最大值确定的,如果最大值有千位,我们必须要计算到千位,如果最多只有十位,我们就计算到十位就可以了,每一位都排序完了之后,数组也就排序成功了,来看一下代码

如果了解基数排序的上面代码可能很容易理解,我们随便找一组代码来测试一下结果吧

看一下运行结果

结果完成正确,看代码不知很直观,我们还是画一个图来帮助我们理解上面的代码吧。

排序的时候先根据个位,然后十位,然后百位,等每个位上的都排序完了之后整个数组也就排序完成了。但是上面代码还不是很完美,因为当出现负数的时候上面代码就没法排序了,我们来想一下当出现负数的时候应该怎么办,话不多说,直接上代码,我们来看一下

其中getMaxNumbit表示返回位数最多的值,也可以说是返回绝对值最大的值,我们看到上面temp加了9,所以如果出现负数就不会报错了,因为每一位只能是从-9~9,总共19个数,当最小-9的时候,再加上9就变为0,下标从0开始,所以也不会出现数组越界异常。

我们找一组数据来测试一下

来看一下运行结果

结果完全正确。


希尔排序也成缩小增量排序,原理是将待排序列划分为若干组,每组都是不连续的,有间隔step,step可以自己定,但间隔step最后的值一定是1,也就说最后一步是前后两两比较。间隔为step的默认划分为一组,先在每一组内进行排序,以使整个序列基本有序,然后再减小间隔step的值,重新分组再排序……不断重复,直到间隔step小于1则停止。还是先看代码

代码还是比较简单的,我们画个图来说明一下

上面的排序其实和冒泡排序很像,只不过冒泡排序是每次都是间隔为1相邻的两个之间进行比较,但希尔排序是间隔为step,还是有一定区别的,我们再看另一种写法

首先它是把待比较的变量保存到temp中,然后往前找,如果前面的比他大,就会把前面的值挪到当前位置,然后再往前找,如果还比当前大那么还挪,直到循环完为止,然后再把当前保存的temp值放到最前面挪动的那个值。这个挪动和前面介绍的插入排序的挪动有点类似,只不过插入排序的挪动是一个个往前比较(二分法插入例外),而这个挪动是每间隔step进行比较然后确定是否挪动。我们上面使用的间隔是数组长度的一半,这个间隔实际上是可以自己定的,但一定要保证间隔最后的一步是1即可,希尔排序中大家都比较认可的一种计算间隔的公式是step = step * 3 + 1;我们再来看一下代码


计数排序是一个非基于比较的排序算法,他首先要找到数组的最大值和最小值然后再根据最大值和最小值申请频率表,其实就是个数组,每个数在数组中出现的频率,我们这里暂且以桶来表示,每个桶对应一个数在原数组中出现的频率,如果一个桶为1就表示和这个桶对应的这个数在原数组中只出现一次,如果为2就表示出现两次……,我们直接看代码

原理就是把对应的数放到对应的桶里,如果桶里已经有值了,说明出现了两个重复的数,桶的值要累加。当每个桶对应值的个数确定以后就把前面的个数不断的累加然后放到后面的桶中,因为当一个数对应后面的桶的时候,他要确定前面有多少比他小的数,然后排序的时候再存放到原数组对应的位置。这里我们是以升序讲解的,比如,当一个数和一个桶对应的时候他需要知道前面究竟有几个比他小的他才能找准在原数组排序的位置,所以前面桶的值要累加到当前桶中,还是看一下图吧

我们随便找一组数据,测试一下运行结果

看一下运行结果

结果正是我们所求的,这就是计数排序。


计数排序是一个非基于比较的排序算法,他首先要找到数组的最大值和最小值然后再根据最大值和最小值申请频率表,其实就是个数组,每个数在数组中出现的频率,我们这里暂且以桶来表示,每个桶对应一个数在原数组中出现的频率,如果一个桶为1就表示和这个桶对应的这个数在原数组中只出现一次,如果为2就表示出现两次……,我们直接看代码

原理就是把对应的数放到对应的桶里,如果桶里已经有值了,说明出现了两个重复的数,桶的值要累加。当每个桶对应值的个数确定以后就把前面的个数不断的累加然后放到后面的桶中,因为当一个数对应后面的桶的时候,他要确定前面有多少比他小的数,然后排序的时候再存放到原数组对应的位置。这里我们是以升序讲解的,比如,当一个数和一个桶对应的时候他需要知道前面究竟有几个比他小的他才能找准在原数组排序的位置,所以前面桶的值要累加到当前桶中,还是看一下图吧

我们随便找一组数据,测试一下运行结果

看一下运行结果

结果正是我们所求的,这就是计数排序。


位图排序也称为bitmap排序,它主要用于海量数据去重和海量数据排序,假如说有10亿个int类型且全部不相同的数据,给1G内存让你排序,你怎么排,如果全部加载到内存中,相当于40亿个字节,大概约等于4G内存。所以全部加载到内存肯定不行,如果我们使用位图排序的话,我们用long类型表示,一个long占用8个字节也就是64位,所以如果我们使用位图排序的话只会占用约0.125G内存,内存占用大大减少。但位图排序有个缺点就是数据不能有重复的,如果有重复的会覆盖掉,这也是位图能在海量数据中去重的原因,我们看下位图排序的代码

我们看到这是使用的是位表示,一个long类型占8个字节,但他可以表示64个数字,所以内存占用会大大减少。最后有个k < array.length的判断,是因为如果有重复的数据会覆盖掉重复的,导致数组变小。但这里面还有个问题就是不能有负数出现,如果出现负数会报异常,我们也可以改一下让负数也可以排序,看代码。

我们来找几行数据测试一下

再来看一下运行的结果


常见的11种排序算法我们都已经分析完了,实际上排序算法并不止我们之前所介绍的那11种,只不过这些算法我们并不常见,有些感觉像闹着玩是的,比如BogoSort,我们来看一下

这种排序基本上没人会用的,他的原理就是每次随机打乱顺序,然后在验证一下是否已经排序好了,如果没有排序好就继续打乱再验证,直到验证排序好为止。并且如果数组的长度超过10的时候,基本上就非常慢了。

除了这种排序以外,还有一种排序叫CocktailSort,简称鸡尾酒排序,应该说它是冒泡排序的一种,他和冒泡排序的不同之处在于,冒泡排序是往一个方向排的,而鸡尾酒排序是往两个方向排的,它是先从左往右把大的排到右边,然后再从右往左把小的排到左边,我们看下代码

每次循环的时候都是把剩余最大的挪到右边,剩余最小的挪到左边。我们也可以换一种写法,分别用两个指针表示,一个指向前面一个指向后面,然后两个指针分别都往中间移,指针走过的地方都是已经排序好的,只要左边指针小于右边指针就一直走下去,看下代码

除了上面两种排序之外还有一种叫鸽巢排序,他和计数排序有点像,我们来看一下代码

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

顾小笙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值