算法原理系列:木桶排序

木桶排序是一种用标记来替代比较操作的排序手段,适用范围较窄,但效率极高,时间复杂度为 O(n) O(n),在生活中,我们也经常能看到一些木桶排序的实际案例,比如扑克牌排序时,我们把它平摊在空间中,这种记录相对位置的排序方法是最直观的木桶排序。

缘由

先来看看,在计算机视角中,如何利用相对位置进行排序操作。给出数据集:

nums = [9,2,1,4,7,8,6]

这样的数据集有明显的特点,nums在指定范围内,所以我们可以建立一个map来映射nums的值和相对位置关系。

map
index: 0 1 2 3 4 5 6 7 8 9
value: 0 1 1 0 1 0 1 1 1 1扫描value中非零元素,得到的排序就是最终的排序结果

代码如下:

    public static void bucketSortWithNoDuplicate(int[] data){int n = data.length;int[] aux = new int[n];int[] map = new int[4 * n]; //开辟足够大的空间for (int i = 0; i < n; i++){map[data[i]]++; }for (int i = 0, k = 0; i < 4 * n; i++){if (map[i] == 0) continue;aux[k++] = i;}//回写for (int i = 0; i < n; i++){data[i] = aux[i];}}

这是最简单的思路,但该排序方法只针对非重复元素的排序,遇到重复元素就不起作用。

如:

nums = 2 5 5 6 8 5 2 6 1 1 6 5map
index : 0 1 2 3 4 5 6 7 8 9
value : 0 2 2 0 0 4 3 0 1 0元素1出现的次数为2次
元素2出现的次数为2次
...做点变形:map[data[i]+1]++;
map
index : 0 1 2 3 4 5 6 7 8 9 10
value : 0 0 2 2 0 0 4 3 0 1 0累加:
map
index : 0  1  2  3  4  5  6  7  8  9  10
value : 0  0  2  4  4  4  8  11 11 12 0物理含义:
元素1出现的下标为1
元素2出现的下标为2
...这样我们就充分利用了map统计的频次,并决定了元素的起始位置在何方。最终排序时,借助aux数组:
for (int i = 0; i < n; i++){aux[map[data[i]]++] = data[i];
}此时,注意map在取映射时并未+1。

这就好比在扑克排序时,预先统计了每张排出现的频次是多少,如2出现3次,3出现2次,则预先给2留三个空位,再给3留5个空位,塞满元素后,也就排序完成。

代码如下:

public static void bucketSortWithDuplicate(int[] data){int n = data.length;int[] map = new int[11];int[] aux = new int[n];//计算出现频率for (int i = 0; i < n; i++){map[data[i]+1]++;}//预留空位for (int i = 1; i < map.length; i++){map[i] = map[i-1] + map[i];}//排序for (int i = 0; i < n; i++){aux[map[data[i]]++] = data[i];}//回写for (int i = 0; i < n; i++){data[i] = aux[i];}}

低位优先排序

在上述基础上,我们可以实现固定位数的radix排序,如:

nums:
791372584
783336045
313758717
165485572
536175826
619392783
231528160
309593813
830298494
219957039思路:
从右到左依次扫描每一位,用木桶排序,因为在针对高位排序时,上述排序稳定,所以不会影响相对位置,这样当遍历到最后一位时,就自然整体有序。

代码如下:

public static void radixSort(long[] data){int n = data.length;int exp = 1;int radix = 10;long max = data[0];while (max/exp != 0){int[] map = new int[11];long[] aux = new long[n];for (int i = 0; i < n; i++){map[(int)(data[i] / exp % 10) + 1]++;}for (int i = 1; i < n; i++){map[i] += map[i-1];}for (int i = 0; i < n; i++){aux[map[(int)(data[i] / exp % 10)]++] = data[i];}for (int i = 0; i < n; i++){data[i] = aux[i];}exp *= radix;}}

针对不同位数的数组排序,低位优先只要明确了数组中最大数的最长位数,同样能达到排序效果。

高位优先排序

再介绍一种高位优先排序,如,给定字符数组:

输入:
she
sells
seashells
by
the
seashore
the
shells
she
sells
are
surely
seashells高位排序,先按第一个字符进行木桶排序,所以有:
0 : are----------
1 : by----------
2 : she
3 : sells
4 : seashells
5 : sea
6 : shore
7 : shells
8 : she
9 : sells
10: surely
11: seashells----------
12: the
13: the所以经过一轮木桶排序后,我们只要能够得到分组后的头index和尾index,便能递归的进行排序操作了。递归,无非就是当首字母相同时,如index(2~11)之间的元素,进行第二轮木桶排序。

代码如下:

    private static int R = 256;private static final int M = 0;private static String[] aux;private static int charAt(String s, int d){if (d < s.length()) return s.charAt(d); else return -1;}public static void sort(String[] data){int N = data.length;aux = new String[N];sort(data, 0, N - 1, 0);}private static void sort(String[] data, int lo, int hi, int d){if (hi <= lo + M){insertionSort(data, lo, hi, d);return;}int[] count = new int[R + 2];for (int i = lo; i <= hi; i++){count[charAt(data[i], d) + 2] ++;}for (int i = 1; i < count.length; i++){count[i] += count[i-1];}for (int i = lo; i <= hi; i++){aux[count[charAt(data[i], d) + 1] ++] = data[i];}for (int i = lo; i <= hi; i++){data[i] = aux[i - lo];}for (int r = 0; r < R; r++){sort(data,lo + count[r], lo + count[r + 1] - 1, d+1);}}public static void insertionSort(String[] elements, int lo, int hi, int d){for (int i = lo + 1; i <= hi; i++){for (int j = i; j > lo && elements[j].charAt(d) < elements[j-1].charAt(d); j--){swap(elements, j, j-1);}}}private static void swap(String[] ele, int i, int j){String tmp = ele[i];ele[i] = ele[j];ele[j] = tmp;}

有些细节很有趣,比如在进行每一轮的木桶排序时,count频率计数是+2开始的,主要原因在于,count[1]这个位置被用来收集字符串末尾的(count[-1+2]),而在数据分类时,遇到末尾的情况直接把顺序照搬至aux即可。

练习:164. Maximum Gap

顺便来道练习题:

Given an unsorted array, find the maximum difference between the successive elements in its sorted form.

Try to solve it in linear time/space.

Return 0 if the array contains less than 2 elements.

You may assume all elements in the array are non-negative integers and fit in the 32-bit signed integer range.

非负整数,所以我们可以采用基数排序来,对数组进行排序,所需要的时间复杂度为 O(n) O(n)

代码如下:

public int maximumGap(int[] nums) {if (nums.length <= 1)return 0;radixSort(nums);int max = 0;for (int i = 1; i < nums.length; i++) {max = Math.max(nums[i] - nums[i - 1], max);}return max;}private static void radixSort(int[] nums) {int exp = 1;int radix = 10;//最大的一定是位数最长的!int max = 0;for (int i = 0; i < nums.length; i++) {max = Math.max(max, nums[i]);}while (max / exp != 0) {int[] count = new int[radix + 1];int[] aux = new int[nums.length];for (int i = 0; i < nums.length; i++) {count[nums[i] / exp % 10 + 1]++;}for (int i = 1; i < count.length; i++){count[i] += count[i-1];}for (int i = 0; i < nums.length; i++){aux[count[nums[i] / exp % 10]++] = nums[i];}for (int i = 0; i < nums.length; i++){nums[i] = aux[i];}exp *= 10;}}

算法原理系列:木桶排序相关推荐

  1. 算法原理系列:红黑树

    红黑树 看了网上关于红黑树的大量教程,发现一个共性,给出定义,适用情况,然后大量篇幅开始讨论它如何旋转,这就一发不可收拾了,各种情况的讨论,插入删除,插入删除,看的云里雾里,好不容易搞清楚,过段时间就 ...

  2. 算法原理系列:优先队列

    算法原理系列:优先队列 第一次总结这种动态的数据结构,一如既往,看了大量的教程,上网搜优先队列原理,能出来一大堆,但不知道为什么怎么这么多人搞不清楚原理和实现的区别?非要把实现讲成原理,今天就说说自己 ...

  3. 机器学习算法原理系列篇4:建模流程(上)

    精彩人工智能相关文章,微信搜索  : robot-learner , 或扫码 机器学习建模流程涉及到几个重要的步骤,如下图所示.在下面的篇章中,我们就每一个步骤展开讨论. 数据收集 获得有效的数据是建 ...

  4. 木桶排序算法_这才是你想要桶排序

    前面说的 昨天发的桶排序文章,有同学回复,确实有比较精明的同学,文章就是想提出一个比较简单的思路,本来想给自己的排序方式写个名字叫做"木桶排序",这篇文章讲解真正的桶排序,结合前面 ...

  5. C++ 十大经典排序算法原理及模板之STL方法实现以及稳定性分析

    写在前面: 1.本文中默认排序为升序,降序的原理类似. 2.如果程序直接复制到vs出现无法识别标记的问题,解决方法在这:vs无法识别标记的解决方法 3.本文的算法都是自己用stl实现的,疏漏之处还请指 ...

  6. java排序算法原理_排序算法原理与实现(java)

    排序算法原理与实现(java) Java程序员必知的8大排序 [来源:本站 | 日期:2012年12月24日 | 浏览173 次] 字体:[大 中 小] 8种排序之间的关系: 1, 直接插入排序 (1 ...

  7. java 排序原理_简单选择排序算法原理及java实现(超详细)

    简单选择排序的原理 简单选择排序的原理非常简单,即在待排序的数列中寻找最大(或者最小)的一个数,与第 1 个元素进行交换,接着在剩余的待排序的数列中继续找最大(最小)的一个数,与第 2 个元素交换.以 ...

  8. 离线强化学习(Offline RL)系列3: (算法篇)策略约束 - BRAC算法原理详解与实现(经验篇)

    论文原文:[Yifan Wu, George Tucker, Ofir Nachum: "Behavior Regularized Offline Reinforcement Learnin ...

  9. SSD系列算法原理讲解----(1)SSD系列算法介绍(主干网络、多尺度Feature Map预测)(笔记)

    SSD系列算法原理介绍 SSD算法介绍: Single Shot MultiBox Detector(One-stage方法) - Wei Liu在ECCV 2016提出 - 直接回归目标类别和位置 ...

最新文章

  1. WebClient.UploadValues Post中文乱码的解决方法
  2. UVa LA 3882 - And Then There Was One 递推,动态规划 难度: 2
  3. Leetcode 117. 填充每个节点的下一个右侧节点指针 II 解题思路及C++实现
  4. python sorted原理_Python sort()和sorted()
  5. unity的vr场景怎么做_如何用Unity快速创建一个VR体验
  6. 系统启动数据库服务器,linux系统如何启动数据库服务器
  7. Judy Beta 第10天
  8. java php json转字符串_php json字符串转为数组或对象
  9. 金笛MODEM池 (RS232串口和RJ45网口)
  10. JavaScript学习(十五)—内部样式与外部样式的修改与设置
  11. c++ list排序_LeetCode.451-根据字符出现频率排序
  12. string类常用方法3
  13. fgo升级经验计算机,命运冠位指定狗粮本每日经验表 fgo国服狗粮本刷取技巧攻略[多图]...
  14. 无限容量还不限速的网盘,了解一下~
  15. 程序员职业发展路径图:从菜鸟工程师到高级架构师(转)
  16. PythonTutor本地化运行
  17. java duplicate key_Stream 操作 Duplicate key问题
  18. 用户发送的eth值msg.value,在合约中以wei为单位。
  19. 抖音三农号创作者具体应该如何做?
  20. kubernetes节点减容与扩容

热门文章

  1. 1688商品详情API接口(item_get-获得1688商品详情接口)
  2. ffmpeg中文版文档
  3. 微信小程序:王者荣耀战力查询微信小程序源码下载支持安卓苹果微信QQ等多区查询
  4. 因特网的三层基础结构 (转)
  5. 什么是SWOT分析法?
  6. 品牌需要来看看这个UP主眼里的“她困境”
  7. 微软官网U启动制作工具安装window10系统
  8. 智能手机 华为 索尼
  9. 为什么小领导讲话都很严厉,大领导都很和蔼?
  10. 大型央企集团财务经营分析框架系列(二)