本篇文章围绕字符串排序的核心思想,通过图示例子和代码分析的方式讲解了两个经典的字符串排序方法,内容很详细,完整代码放在文章的最后。

一、键索引计数法

  在一般排序中,都要用里面的元素不断比较,而字符串这玩意儿大可不必比较,有另外一种思想。在键索引计数法中,可以突破NlongN的排序算法运行时间下限,它的时间级别是线性的! 

  

引入字母表概念: 

  想要不对字符串里面的字符进行对比,我们需要引入字母表的概念,比如将‘a’看作1,‘b’看作2,‘c’看作3,这样下去,26个字母只需要一个长度为27的数组就能够表示(下标为0不用),而且按数字来看他们是有序的(从a到z对应1到26)。

  所以“abcdefg..”这些字符转换为整型时(使用charAt()函数),自然有一个对应的顺序,所以我们只需要找到一个合适大小的数组来保存每个将会用到的字符的信息即可。现在我们创建count[]数组,大小为256,用来保存对应字符出现的频率和排序时的索引。

  索引计数法共分为四步,下面进行说明并举例。(用R来表示字符的种类,r表示字符在R中的顺序)

1、计算频率:

for(int i=0;i<N;i++){//计算频率
count[a[i].charAt(d)+1]++;
}

遍历所有字符串,d为字符串的第d个字符(下面例子中字符串都为单个数字)。

出现什么字符,我们就将对应的count[r+1]加一(里面为什么是r+1,看到下一步你自然会明白)。

2、计算索引:

for(int r=0;r<R;r++){//将频率转换为索引
count[r+1]+=count[r];
}

需要在我们计算出频率的基础上进行:count[r+1]+=count[r]

将count数组中后一位总是加上前一位。

  

例子

老师组织了一次游玩,把同学们分为四组,需要做的是将同学按组号排序(这里R为4,count数组大小为R+2,下标为0不用)

              图1 计算出现频率

            图2 将频率转换为起始索引

可以从图二最后一行看到,r为1对应索引为0,即一组从0开始排序。

r为2对应索引1,即二组从1开始排序。

而第三组索引为5,说明从一到四全是第二组的位置。

3、数据分类

for(int i=0;i<N;i++){//数据分类
aux[count[a[i].charAt(d)]++]=a[i];

进行数据分类我们需要一个辅助数组aux,用来暂时储存排序的数据。

把数据放入辅助字符串数组,全部放入时已经形成有序。

4、回写

for(int i=0;i<N;i++){//回写
a[i]=aux[i];
}

把辅助字符串数组的内容搬回去就行了。

到此为止键索引计数法就完成了,接下来利用它来实现LSD/MSD。

二、低位优先排序(LSD)

第位优先排序与高位优先排序的主要区别在于排序的方向,核心思想算法都是通过键索引计数法。低位优先算法是从字符串的右到左来排序(这可能会出现一些问题,在高位优先排序的介绍中将会提到)。

下图为一个地位优先排序的完整过程:

利用索引计数法,从左到右对每一位进行索引计数,这就形成了第位优先排序。

for (int d=W-1;d>=0;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)

在低位优先排序中,可能会出现一点问题。比如字符串“ab”与“ba”,长度为2需要进行两次排序,第一次排序结果为“ba”、“ab”,第二次排序结果为“ab”、“ba”,第一次排序的结果对第二次毫无意义,这就造成了时间上的浪费。

而在高位优先排序中,只会进行一次排序。结果为“ab”、“ba”。

不同之处:

在高位排序中又引入了分组的概念,即用首字母来切分下一个排序组。

在代码中我们使用递归的方式来不断切分排序组。

 public static void sort(String[] a,int lo,int hi,int d){
if(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=0;i<=hi;i++){
aux[count[charAt(a[i],d)+1]++]=a[i];
}
for(int i=0;i<=hi;i++){
a[i]=aux[i];
}
for(int r=0;r<R;r++){
sort(a,lo+count[r],lo+count[r+1]-1,d+1);
}
}

上面这段代码非常简洁,但其中有一些地方是复杂的,请研究下面例子的调用过程确保你理解了算法。

                          图3 sort(a,0,9,0)的顶层调用

在下一期带来另一种字符串排序方法,三向字符串快速排序,相比于这两种方法,将会有更广的受用面!

四、完整代码

 public class LSD {
public static void sort(String[] a,int W){//W表示字符串的长度
int N=a.length;
int R=256;//依字符的种类数目而定
String aux[]=new String[N];
for (int d=W-1;d>=0;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];
}
}
}
}
 public class MSD {
private static int R=256;
private static String[] aux; public 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);
} public static void sort(String[] a,int lo,int hi,int d){
if(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=0;i<=hi;i++){
aux[count[charAt(a[i],d)+1]++]=a[i];
}
for(int i=0;i<=hi;i++){
a[i]=aux[i];
}
for(int r=0;r<R;r++){
sort(a,lo+count[r],lo+count[r+1]-1,d+1);
}
}
}

字符串之————图文讲解字符串排序(LSD、MSD)的更多相关文章

  1. [原]Java面试题-将字符串中数字提取出来排序后输出

    [Title][原]Java面试题-将字符串中数字提取出来排序后输出 [Date]2013-09-15 [Abstract]很简单的面试题,要求现场在纸上写出来. [Keywords]面试.Java. ...

  2. 编写一个类,其中包含一个排序的方法Sort(),当传入的是一串整数,就按照从小到大的顺序输出,如果传入的是一个字符串,就将字符串反序输出。

    namespace test2 { class Program { /// <summary> /// 编写一个类,其中包含一个排序的方法Sort(),当传入的是一串整数,就按照从小到大的 ...

  3. strcmp()函数-比较字符串的大小、字符串排序

    1.比较字符串的大小: 用法:strcmp(字符串1,字符串2),若字符串1>字符串2 则返回1,字符串1<字符串2 则返回 -1,相等返回0. 比较两个字符串的算法是:逐个比较两个串中对 ...

  4. 字符串之————三向字符串快速排序(Quick3string)

    上一篇介绍了字符串的两种经典排序方法(LSD MSD): https://www.cnblogs.com/Unicron/p/11531111.html 下面我们来介绍一种通用的字符串排序方法——三向 ...

  5. 【转】android 最新 NDK r8 在window下开发环境搭建 安装配置与使用 详细图文讲解,完整实际配置过程记录(原创)

    原文网址:http://www.cnblogs.com/zdz8207/archive/2012/11/27/android-ndk-install.html android 最新 NDK r8 在w ...

  6. android 最新 NDK r8 在window下开发环境搭建 安装配置与使用 详细图文讲解,完整实际配置过程记录(原创)

      android 最新 NDK r8 在window下开发环境搭建 安装配置与使用 详细图文讲解,完整实际配置过程记录(原创) 一直想搞NDK开发却一直给其他事情耽搁了,参考了些网上的资料今天终于把 ...

  7. Swift3.0语言教程使用字符串创建和初始化字符串

    Swift3.0语言教程使用字符串创建和初始化字符串 Swift3.0语言教程使用字符串创建和初始化字符串,在编程语言中,字面值是很常见的数据描述形式.人们可以通过字面所表达的意思,获知其含义,尤其是 ...

  8. BAT批处理中的字符串处理详解(字符串截取)

    BAT批处理中的字符串处理详解(字符串截取)   BAT批处理中的字符串处理详解(字符串截取 批处理有着具有非常强大的字符串处理能力,其功能绝不低于C语言里面的字符串函数集.批处理中可实现的字符串处理 ...

  9. 【转】BAT批处理中的字符串处理详解(字符串截取)

    下面对这些功能一一进行讲解. 1.截取字符串 截取字符串可以说是字符串处理功能中最常用的一个子功能了,能够实现截取字符串中的特定位置的一个或多个字符.举例说明其基本功能: @echo off set ...

随机推荐

  1. Kafka 系列(二)—— 基于 ZooKeeper 搭建 Kafka 高可用集群

    一.Zookeeper集群搭建 为保证集群高可用,Zookeeper 集群的节点数最好是奇数,最少有三个节点,所以这里搭建一个三个节点的集群. 1.1 下载 & 解压 下载对应版本 Zooke ...

  2. 神盘GCCX,2019必撸大毛!

    自从今年5月转型投资以来,已经很少薅羊毛了! 不是不撸,是因为一般的羊毛我真看不上! 撸羊毛能不能发财,能不能日入几百几千! 答案是,可以! 干羊毛,像趣步,云钱包,云比特,环保币,很多人都发财了!前 ...

  3. ECMAScript---数字类型详解

    number数字类详解 整数.小数.负数.NaN都是number数字类型的 NaN:not a number ,但是它是数字类型的 isNaN:检测当前值是否 不是有效数字,返回true代表不是有效数 ...

  4. 学习js都学习什么?

    前言:js(javaScript)是面向对象(OOP)的编程语言,目前不仅仅是客户端语言了,基予node可以做服务器端程序,那我们学习js都学习什么? 学习js,我们学习它的几部分组成 1.ECMAS ...

  5. JavaScript 数据结构与算法之美 - 十大经典排序算法汇总(图文并茂)

    1. 前言 算法为王. 想学好前端,先练好内功,内功不行,就算招式练的再花哨,终究成不了高手:只有内功深厚者,前端之路才会走得更远. 笔者写的 JavaScript 数据结构与算法之美 系列用的语言是 ...

  6. Django之上传图片,分页,三级联动

    Django1.8.2中文文档:Django1.8.2中文文档 上传图片 配置上传文件保存目录 1)新建上传文件保存目录. 2)配置上传文件保存目录. 后台管理页面上传图片 1)设计模型类. 2)迁移 ...

  7. 云原生生态周报 Vol. 17 | Helm 3 发布首个 beta 版本

    本周作者 | 墨封.衷源.元毅.有济.心水 业界要闻 1. Helm 3 首个 beta 版本 v3.0.0-beta.1 发布 该版本的重点是完成最后的修改和重构,以及移植其他 Helm 2 特性. ...

  8. CodeForces 1042 F Leaf Sets 贪心

    Leaf Sets 题意:给你一棵树,树上有n个点,只有一条边的点叫做叶子,现在要求把所有的叶子分组,每个组内的所有叶子的距离都不能大于k. 题解: 我们可以随意找一个不是叶子的节点当做这颗树的根节点 ...

  9. Prometheus安装

    Prometheus安装 下载地址: https://prometheus.io/download/ 现在时间是: 2019.09.07 安装环境: Linux centos7 minimal 虚拟机 ...

  10. NOIP 2009 最优贸易 题解

    一道最短路的题,找一个买入和卖出相差最高的点即可,我们先以1为起点跑spfa,d1[x]不再表示距离而表示能够经过权值最小的节点的权值即 if(d1[y]>min(d1[x],price[y]) ...