一、低位优先(Least-Signifcant-Digit First,LSD)

字符串的低位优先排序算法目的就是将一组字符串按照从右到左的顺序依次比较指定索引位置的字符大小并排序。根据上述字符串的分组算法的逻辑,很容易使用下面的代码实现:下面的代码实质上就是将一组字符串按照倒数第一个字符分组(最后一个字符相同的,分组前后相对顺序不变),接着再按照倒数第二个字符分组(倒数第二个字符相同的,分组前后相对顺序不变),直到最终按照倒数第n个字符分组完毕。

    public static void sortByLSD(String[] a, int W) {int N = a.length;int R = 256;   // 假设字符串中的字符都来自于ASCII字母表String[] temp = new String[N];for (int d = W-1; d >= 0; d--) {// 对于第d个字符,按照该字符代表的数字大小对字符数组进行排序// 统计出现频率int[] count = new int[R+1];for (int i = 0; i < N; i++)count[a[i].charAt(d) + 1]++;// 计算放入临时数组的起始索引for (int r = 0; r < R; r++)count[r+1] += count[r];// 对字符串元素进行分类for (int i = 0; i < N; i++)temp[count[a[i].charAt(d)]++] = a[i];// 回写for (int i = 0; i < N; i++)a[i] = temp[i];}}

二、高位优先(MSD)的字符串排序

它不要求被排序的字符串等长,而且不一定需要检查所有的输入就能完成排序。该算法将从左开始向右检查字符(就像通常我们比较字符串那样),使用和快速排序类似的方法将字符串排序。
它是从左向右检查每个字符,统计字符串首字母的频率,并按其来进行归类、排序,然后对归类后的字符串:将所有首字母相同的归为一个子数组,递归地分别对这些子数组排序。精炼点说就是:

  • 以首字母来排序,将数组切分成首字母相同的子数组
  • 忽略都相同的首字母,递归地排序子数组
    // 返回字符串制定索引位置的字符,如果索引位置值等于字符串长度值,则返回-1private static int charAt(String s, int d) {assert d >= 0 && d <= s.length();if (d == s.length())return -1;return s.charAt(d);}private static void sort(String[] a, int lo, int hi, int d, String[] temp) {if (hi <= lo) {return;}int R = 256;// 计算出现频率int[] count = new int[R + 2];// 加2是因为把超出字符串索引找不到的字符也当做一个字符,注意低位优先算法是+1for (int i = lo; i <= hi; i++) {int c = charAt(a[i], d);count[c + 2]++;}// 计算迁移到临时数组的起始索引for (int r = 0; r < R + 1; r++)count[r + 1] += count[r];// 对字符串进行分类for (int i = lo; i <= hi; i++) {int c = charAt(a[i], d);temp[count[c + 1]++] = a[i];}// 回写for (int i = lo; i <= hi; i++)a[i] = temp[i - lo];// 递归的以各个字符进行分类for (int r = 0; r < R; r++)sort(a, lo + count[r], lo + count[r + 1] - 1, d + 1, temp);}// 交换a[i] 与 a[j] 两个字符串private static void exch(String[] a, int i, int j) {String temp = a[i];a[i] = a[j];a[j] = temp;}public static void sortByMSD(String[] a) {int N = a.length;String[] temp = new String[N];sort(a, 0, N - 1, 0, temp);}public static void main(String... args) {String[] a = new String[] { "aaaabbbb", "bbbbbbbb", "ccccdddd", "ccccaaaa" };sortByMSD(a);System.out.println(Arrays.toString(a));}

三、三向快速的字符串排序 (扩展)

快速排序在实际应用中会面对大量具有重复元素的数组,因此我们可以改进排序算法,我们对键的首字母进行三向切分,然后递归地将三个子数组进行排序。三向字符串快速排序是一个字符串排序的通用算法,最多的情况适用于含有公共前缀的字符串。

三向切分是个啥:将数组分为三部分:小于当前切分元素的部分,等于当前切分元素的部分,大于当前切分元素的部分。

E.W.Dijlstra(对,就是Dijkstra最短路径算法的发明者)曾经提出一个与之相关的荷兰国旗问题(一个数组中有分别代表红白蓝三个颜色的三个主键值,将三个主键值排序,就得到了荷兰国旗的颜色排列)。

他提出的算法是: 对于每次切分:从数组的左边到右边遍历一次,维护三个指针lt,gthe i,其中

  • lt指针使得元素(arr[0]-arr[lt-1])的值均小于切分元素;
  • gt指针使得元素(arr[gt+1]-arr[N-1])的值均大于切分元素;
  • i指针使得元素(arr[lt]-arr[i-1])的值均等于切分元素,(arr[i]-arr[gt])的元素还没被扫描,切分算法执行到i>gt为止。

每次切分之后,位于gt指针和lt指针之间的元素的位置都已经被排定,不需要再去移动了。之后将(lo,lt-1),(gt+1,hi)分别作为处理左子数组和右子数组的递归函数的参数传入,递归结束,整个算法也就结束。
三向切分的示意图:

代码如下:

    public static void sort(String[] a) {sort(a, 0, a.length-1, 0);}private static int charAt(String s, int d) { assert d >= 0 && d <= s.length();if (d == s.length()) return -1;return s.charAt(d);}//对a[lo..hi]范围的字符按照第d个字符排序或分类private static void sort(String[] a, int lo, int hi, int d) { if (hi <= lo) {return;}int lt = lo, gt = hi;int v = charAt(a[lo], d);int i = lo + 1;while (i <= gt) {int t = charAt(a[i], d);if      (t < v) exch(a, lt++, i++);else if (t > v) exch(a, i, gt--);else              i++;}/**看下面的代码,知道为什么称为三向排序了吧? */sort(a, lo, lt-1, d);  //排序a[lo..lt-1]返回的字符串if (v >= 0) sort(a, lt, gt, d+1); //排序a[lt..gt]范围的字符串sort(a, gt+1, hi, d); //排序a[gt+1..hi]范围的字符串}private static void exch(String[] a, int i, int j) {String temp = a[i];a[i] = a[j];a[j] = temp;}public static void main(String[] args) {String[] a = new String[] { "aaaabbbb", "bbbbbbbb", "ccccdddd", "ccccaaaa" };sort(a);System.out.println(Arrays.toString(a));}

相对于高位优先字符串算法的优点

  1. 高位优先字符串算法可能会创建许多的空数组(前缀相同的情况下),但本算法总是只有三个;
  2. 本算法不需要额外的空间。
  3. 要将含有N个字符串的数组排序,三向字符串快速排序需要比较字符~NlnN次。
  4. 对于包含大量重复元素的数组,这个算法将排序时间从线性对数级降到了线性级别。

我的微信公众号:架构真经(id:gentoo666),分享Java干货,高并发编程,热门技术教程,微服务及分布式技术,架构设计,区块链技术,人工智能,大数据,Java面试题,以及前沿热门资讯等。每日更新哦!

参考资料:

  1. https://blog.csdn.net/nmgrd/article/details/52014235
  2. https://www.cnblogs.com/xiaofeixiang/p/4857404.html
  3. https://cloud.tencent.com/developer/article/1139660
  4. https://www.jianshu.com/p/d70aeccaee19

程序员的算法课(10)-字符串排序算法实例(纯代码)相关推荐

  1. 程序员面试金典——18.10字符串变换

    程序员面试金典--18.10字符串变换 Solution1: 我的答案.穷举法,个人认为此题还是有点难度的... 利用了倒推法以及很高的时间复杂度才解决,并不值得推崇呀. class Change { ...

  2. 别翻了,程序员必学十大经典排序算法,看这篇就够了

    说明 十大排序算法可以说是每个程序员都必须得掌握的了,花了一天的时间把代码实现且整理了一下,为了方便大家学习,我把它整理成一篇文章,每种算法会有简单的算法思想描述,为了方便大家理解,我还找来了动图演示 ...

  3. 数据结构与算法--经典10大排序算法(动图演示)【建议收藏】

    十大经典排序算法总结(动图演示) 算法分类 十大常见排序算法可分为两大类: 比较排序算法:通过比较来决定元素的位置,由于时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序 非比较类型排 ...

  4. 大数据算法系列10:字符串检验算法

    文章目录 一. 字符串检验算法 二. 练习 2.1 面试题(输出字符串的排列组合) 2.2 POJ2262(求奇质素之和) 2.3 开门游戏 2.4 Uva106(费马大定律) 2.5 POJ3744 ...

  5. Java常用排序算法/程序员必须掌握的8大排序算法

    本文由网络资料整理而来,如有问题,欢迎指正! 分类: 1)插入排序(直接插入排序.希尔排序) 2)交换排序(冒泡排序.快速排序) 3)选择排序(直接选择排序.堆排序) 4)归并排序 5)分配排序(基数 ...

  6. 【数据结构与算法】程序员们常用的10个关键数据结构,包括它们的原理和C语言实现代码

    [数据结构与算法]程序员们常用的10个关键数据结构,包括它们的原理和C语言实现代码 文章目录 [数据结构与算法]程序员们常用的10个关键数据结构,包括它们的原理和C语言实现代码 1. 数组 (Arra ...

  7. 好程序员web前端分享数组及排序、去重和随机点名

    好程序员web前端分享数组及排序.去重和随机点名,栈堆结构:堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除. 栈:存放的是路径:容量有限(在一开始被定义之 ...

  8. 原创 | 2020年Java程序员应该学习的10大技术

    对于Java开发人员来说,最近几年的时间中,Java生态诞生了很多东西.每6个月更新一次Java版本,以及发布很多流行的框架,如Spring 5.Spring Security 5和Spring Bo ...

  9. 排序算法,最全的10大排序算法详解(Sort Algorithm)

    文章目录 排序算法,最全的10大排序算法详解(Sort Algorithm) 排序算法分类 排序算法稳定性 时间复杂度(time complexity) 1#时间复杂度的意义 2#基本操作执行次数 如 ...

  10. PHP程序员应该掌握的10项技能

    一个PHP程序员应该掌握的10项技能!  很多学PHP的人一直也搞不清楚,一个PHP程序员和Java程序员或者是.net程序员有什么不同,告诉你,其实都一样!没有什么不同,下面的内容,就是针对一个Ja ...

最新文章

  1. 大型数据库设计原则与技巧
  2. ★核心关注点_《信息系统项目管理师考试考点分析与真题详解》
  3. ansible之playbook
  4. form 提交多个对象及springMVC接收
  5. mac 安装 RabbitMQ
  6. 设计模式复习-原型模式
  7. 深度学习目标检测系列:RCNN系列算法图解
  8. 图文详解 Kubernetes,刺激…
  9. [html] http中的301、302、307、308有什么区别?
  10. python3.7界面_Python3.7+tkinter实现查询界面功能
  11. excel按条件查询mysql_Excel中实现多条件查找的15种方法
  12. 反向传播算法的详细解释(上)
  13. 【机器学习实战】垃圾分类快速理解机器学习中的朴素贝叶斯(Naive Bayes)
  14. ArcGIS中修改面图层中相邻面的公共边
  15. 关于黑莓手机 三键重启的操作方法
  16. whatweb tree
  17. HTML5中weight属性的作用,weight属性
  18. matlab数值微分与数值积分
  19. 怎么做一个可执行的生产计划排程?
  20. 软技能:代码之外的生存指南

热门文章

  1. MyEclipse下连接Mysql
  2. MySQL行锁 表锁理解
  3. 备份和迁移Kubernetes应用程序利器-velero
  4. Octavia API接口慢问题排查引发的思考
  5. python每隔30s检查一次_Python的全局解释器锁
  6. selenium python文档_selenium+python实现百度文库word文档抓取
  7. RabbitMQ 延迟插件的作用
  8. spring事务管理中,用try-catch处理了异常,事务也会回滚?
  9. System.arraycopy()实现数组之间的复制
  10. ip netns的使用及network namespace 简介