Java字符串排序

文章目录

  • Java字符串排序
    • 排序方法概述
    • 键索引计数法
    • 低位优先的字符串排序(LSD)
    • 高位优先的字符串排序(MSD)
    • 三向字符串快速排序

排序方法概述

对于许多应用,决定顺序的键都是字符串。本篇讲述如何利用字符串的特殊性质来对其进行高效的排序。

第一类方法会从右到左检查键中的字符。这种方法一般被称为低位优先Least-Significant-DigitFirstLSD)的字符串排序。如果将一个字符串看做一个256进制的数字,那么从右向左检查字符串就等价于先检查数字的最低位。这种方法最适合用于键的长度都相同的字符串排序应用。

第二类方法会从左到右检查键中的字符,首先查看的是最高位的字符。这种方法通常称为高位优先MSD)的字符串排序。高位优先的字符串排序和快速排序类似,因为它们都会将需要排序的数组切分为独立的部分并递归地用相同的方法处理子数组来完成排序。它们的区别之处在于高位优先的字符串排序算法在切分时仅使用键的第一个字符,而快速排序的比较则会涉及键的全部。

第三种方法是高位优先的字符串排序算法的改进快速排序,根据键的首字母进行三向切分,仅在中间子数组中的下一个字符(因为键的首字母都与切分字符相等)继续递归排序。

键索引计数法

作为热身,我们先学习一种适用于小整数键的简单排序方法。这种叫做键索引计数的方法本身就很实用,同时也是要学习的三种排序算法中前两种的基础。它其实就桶计数

现在来情景引入,老师在统计学生的分数时可能会遇到以下数据处理问题。学生被分为若干组,标号为1、2、3、4等。在某些情况下,我们希望将全班同学按组分类。因为组的编号是较小的整数,使用键索引计数法来排序时很合适的。假设数组a[]中的每个元素都保存了一个名字和一个组号,其中组号在0到R-1之间,代码a[i].key()会返回指定学生的组号。四个步骤见代码

int N = a.length;
int R = 256;    //R为字符基数String[] aux = new String[N];
int[] count = new int[R + 1];//计算出现频率
for (int i = 0; i < N; i++) count[a[i].key() + 1]++;//将频率转换为索引
for (int r = 0; r < R; r++) count[r + 1] += count[r];//将元素分类
for (int i = 0; i < N; i++) aux[count[a[i].key()]++] = a[i];//回写
for (int i = 0; i < N; i++)a[i] = aux[i];

命题A:键索引计数法排序N个键为0R-1之间的整数的元素需要访问数组11N+4R+1

低位优先的字符串排序(LSD)

如果字符串的长度均为W,那就从右向左以每个位置的字符作为键,用键索引计数法将字符串排序W遍。

命题B:低位优先的字符串排序算法能够稳定地将定长字符串排序

class LSD{// Least-Significant-Digit First//低位优先的字符串排序(基数排序)public static void sort(String[] a, int W) {//通过前W个字符将a[]排序int N = a.length;int R = 256;    //基数String[] aux = 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++) aux[count[a[i].charAt(d)]++] = a[i];//回写for (int i = 0; i < N; i++) a[i] = aux[i];}}
}

在许多字符串排序的应用中,键的长度可能互不相同。改进后的低位优先的字符串排序是可以适应这些情况的。下来讲解两种处理变长键排序的算法

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

首先用键索引计数法将所有字符串按照首字母排序,然后(递归地)再将每个首字母所对应的子数组排序(忽略首字母,因为每一类中的所有首字母都是相同的)。和快速排序一样,高位优先的字符串排序会将数组切分为能够独立排序的子数组来完成排序任务,但它的切分会为每个首字母得到一个子数组,而不是像快速排序中那样产生固定的两个或者三个切分。

在高位优先的字符串排序算法中,要特别注意到达字符串末尾的情况。在排序中,合理的做法是将所有字符都已被检查过的字符串所在的子数组排在所有子数组的前面,这样就不需要递归地将该子数组排序。为了简化这两步计算,我们使用了一个接受两个参数的私有方法charAt()来将字符串中字符索引转化为数组索引,当指定的位置超过了字符串末尾时该方法返回-1,。然后将所有返回值加1,得到一个非负的int值并用它作为count[]的索引。这种转换意味着字符串中的每个字符都可能产生R+1种不同的值:0表示字符串的结尾,1表示字符串的第一个字符,2表示字符串的第二个字符,等等。因为建索引计数法本来就需要一个额外的位置,所以使用代码int count[] = new int[R + 2]

class MSD{//高位优先的字符串排序private static int R = 256;      //基数private static final int M = 15; //小数组的切换阈值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[] a) {int N = a.length;aux = new String[N];sort(a, 0, N - 1, 0);}private static void sortInsert(String[] a, int lo, int hi) {//小型数组进行插入排序for (int i = lo + 1; i <= hi; i++) {for(int j = i; j > lo && a[j].compareTo(a[j - 1]) < 0; j--) {String tmp = a[j];a[j] = a[j - 1];a[j - 1] = tmp;}}}private static void sort(String[] a, int lo, int hi, int d) {//以第d个字符为键将a[lo]至a[hi]排序if(hi <= lo + M) {sortInsert(a, lo, hi);return; }int [] count = new int[R + 2];    //计算频率for(int i = lo; i <= hi; i++) {count[charAt(a[i], d) + 2]++;}for(int r = 0; r < R + 1; r++) {  //将频率转换为索引count[r + 1] += count[r];}for(int i = lo; i <= hi; i++) {   //数据分类aux[count[charAt(a[i], d) + 1]++] = a[i];}for(int i = lo; i <= hi; i++) {   //回写a[i] = aux[i - lo]; }//递归的以每个字符为键进行排序for(int r = 0; r <R; r++) {sort(a, lo + count[r], lo + count[r + 1] - 1, d + 1);}}
}

三向字符串快速排序

我们也可以根据高位优先的字符串排序算法改进快速排序,根据键的首字母进行三向切分,仅在中间子数组的下一个字符(因为键得出首字母都与切分字母相同)继续递归排序。这个算法的实现并不困难,参考往期排序算法中的三向切分快排即可。

尽管排序的方式有所不同,但三向字符串快速排序根据的仍然是键的首字母并使用递归的方法将其余部分排序。对于字符串的排序,这个方法比普通的快速排序和高位优先的字符串排序更友好。实际上,它就是两种算法的结合。

三向字符串快速排序只将数组切分为三部分,因此当相应的高位优先的字符串排序产生的非空切分较多时,它需要移动的数据量就会变大,因此它需要进行一系列的三向切分才能够取得多向切分的效果。但是,高位优先的字符串排序可能会创建大量(空)子数组,而三向字符串快速排序的切分总是只有三个。因此三向字符串快速排序能够很好地处理等值键、有较长公共前缀的键、取值范围较小的键和小数组-----所有高位优先的字符串排序算法不擅长的各种情况。

class Quick3string{//三向字符串快速排序private static int charAt(String s, int d) {if(d < s.length()) {return s.charAt(d);}return -1;}public static void sort(String[] a) {sort(a, 0, a.length - 1, 0);}private static void sort(String[] a, int lo, int hi, int d) {if(hi <= lo) {return;}int lt = lo, gt = hi, i = lo + 1;int v = charAt(a[lo], d);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++;}}//a[lo..lt-1] < v = a[lt..gt] < a[gt+1..hi]sort(a, lo, lt - 1, d);if(v >= 0) {sort(a, lt, gt, d + 1);}sort(a, gt + 1, hi, d);}private static void exch(String[] a, int i, int j) {String t = new String(a[i]);a[i] = a[j];a[j] = t;}
}

在将字符串数组a[]排序时,根据它们的首字母进行三向切分,然后(递归地)将得到的三个子数组排序:一个含有所以首字母小于切分字符的字符串子数组,一个含有所以首字母等于切分字符串的子数组(排序时忽略它们的首字母),一个含有所有首字母大于切分字符的字符串的子数组。


参考资料:《算法》第四版

Java三种方法实现字符串排序相关推荐

  1. java三种方法拆分字符串

    字符串分割有三种方法,如下: 1.用split()方法进行分割,分割开的子字符串放入数组,然后进行处理. 示例代码如下: public class SplitTest { public static ...

  2. Java中用三种方法输出字符串_java中两个字符串连接的三种方法

    java中两个字符串连接有以下三种方法: 第一种方法:使用+: 第二种方法:使用concat(): 第三种方法:使用append(): 如下代码: public class Practice { // ...

  3. java持久层用文件_JAVA中用三种方法将字符串持久化到文件中

    经常需要将数据进行持久化,而我们的文件系统是最古老也是最可靠的保存方式.这里就给出一个在JAVA中把字符串保存到文件中的例子.如下: package test.base; import java.io ...

  4. 三种方法求字符串长度

    在求字符串长度时,第一时间会想到运用strlen这个函数,但对于初学者来说,除了掌握这个函数之外,还应当自己写函数来实现相似的功能. 接下来我列举了三种方法来求字符串长度,供大家参考: (1)运用st ...

  5. centos 安装JAVA 三种方法

    2019独角兽企业重金招聘Python工程师标准>>> 由于各Linux开发厂商的不同,因此不同开发厂商的Linux版本操作细节也不一样,今天就来说一下CentOS下JDK的安装: ...

  6. Java三种多线程实现睡眠排序

    原文链接:https://5hx.ink/?p=168 1.继承Thread类,重写run( )方法 2.实现Runnable接口,重写run( )方法 3.实现Callable接口,重写call( ...

  7. 三种方法reverse字符串

    1.自己编写函数,不调用库函数 首先当然是自己写函数实现啦,不调用库函数来反转 #include <iostream> #include <string> using name ...

  8. js 把字符串转成json对象的三种方法

    js 把字符串转成json对象的三种方法 不管字符串是否含有转义字符,都能转换成 Json 对象 1, js自带的eval函数,其中需要添加小括号eval('('+str+')'); function ...

  9. pca算法python代码_三种方法实现PCA算法(Python)

    主成分分析,即Principal Component Analysis(PCA),是多元统计中的重要内容,也广泛应用于机器学习和其它领域.它的主要作用是对高维数据进行降维.PCA把原先的n个特征用数目 ...

  10. java equals 判断空_Java 判断字符串是否为空的三种方法与性能分析

    [java中判断字符串是否为数字的三种方法  1>用JAVA自带的函数 public static boolean isNumeric(String str){   for (int i = s ...

最新文章

  1. KVM虚拟化实践(一)
  2. 浅谈阀控型铅酸蓄电池在数据中心的应用与日常管理
  3. 27.4. /etc/bandwidthd.conf
  4. 阿里云低延时直播RTS能力升级,让直播推流效果更佳
  5. PyTorch深度学习实践06
  6. javaweb(二十一)——JavaWeb的两种开发模式
  7. mysql订单表上亿怎么分表_[转]单表上亿的数据量如何分表
  8. 黄聪:主机宝安装wordpress注意事项
  9. 《Essential C++》笔记之迭代器Iterator(泛型指针)
  10. linux用mame玩游戏,Ubuntu下用kxmame运行精彩的街机游戏
  11. 带有天气预报的高大上web报表制作分享
  12. 周鸿祎:做产品体验先把自己切换到二傻子模式
  13. 【ProjectT】Tapestry • Quick Start • Forms
  14. mysql 5.7.14 在 windows 下的配置
  15. 十、垃圾回收策略概览
  16. python期权价格计算器_使用Python构建内在价值计算器
  17. java导出excel设置单元格样式_java poi批量导出excel 设置单元格样式
  18. 含泪整理最优质Fbx 3d模型素材,你想要的这里都有
  19. video sematic segmentation视频语义分割方向相关论文罗列+数据集下载链接
  20. 坐月子“一嫂难求”,月嫂为何这么有“钱景”?

热门文章

  1. 教你写页游自动化Python脚本,取色,大漠识别和后台点击
  2. 怎样将网络机顶盒usb调试模式打开
  3. 如何在手机上查看APP原型
  4. C语言:段错误产生原因及简单的调试方法
  5. SubSonic 安装与使用
  6. Word,Excel联动结合邮件合并功能实现批量打印员工个人信息表桌牌奖状出试卷
  7. CPU缓存侧信道攻击
  8. plecs matlab 联合仿真,基于Matlab和PLECS的电力电子仿真实验教学
  9. 【js】碰到了Flash与extjs冲突无法输入中文解决办法。
  10. 小D课堂-nexus