53.String sort 字符串排序的几种方法

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

简介

在之前的一些排序算法中,主要是对一些数值的类型比较的比较多一点。而对于字符串类型来说,它有一些特殊的性质。如果按照传统的排序方法,对于字符串的比较性能其实还取决于字符串的长度以及相似程度。实际上,对于一些字符集的取值在一个比较小的范围内的情况,我们可以有一些比较高效率的算法。这里针对这些特殊的情况进行讨论。

假设给定的排序集合里元素,也就是每个字符都是在一个比较有限的范围里,比如说256个字符范围内。那么,我们可以利用这个特性做一些高效的处理。联想到之前讨论过的counting sort和radix sort方法。这里就是利用了这个特性。

Key-Indexed counting

在之前讨论couting sort的文章里,曾经针对需要排序的元素为数字的情况进行过讨论。counting sort成立的一个前提是它里面所有的元素取值是在一个固定的范围内。假设这个数组里元素能取的最大值是k,那么每次我们要排序的时候只需要声明一个长度为k的数组a。每次碰到一个元素i就将a[i]对应的值加1。这样就统计出来了所有从小到大的元素的值的分布。剩下的就只是从小到达把这些值重新排列输出就可以了。

当然,在一些数字有一定长度而且它们的长度都一样的情况下。我们可以利用从高到低或者从低到高位逐位排序的方式来对数组进行排序。这就是radix sort的基本思路。它本质上就是在每一位的排序上都使用了couting sort。

借鉴前面对于数字的排序,我们对于字符串数组的排序也可以采用类似的方式:

Java代码

1.int[] count = new int[R + 1];

2.//计算每个字符出现的频率

3.for(int i = 0; i < n; i++)

4. count[a[i].charAt(d) + 1]++;

5.//将每个字符出现的频率转换为所在的索引

6.for(int r = 0; r < R; r++)

7. count[r + 1] += count[r];

8.//将字符分布到具体的数组位置

9.for(int i = 0; i < n; i++)

10. aux[count[a[i].charAt(d)]++] = a[i];

11.//将结果拷贝回数组

12.for(int i = 0; i < n; i++)

13. a[i] = aux[i];

上述代码里的R表示当前字符的取值范围。在R值不大的时候它的效率还是相当可观的。在这个计数排序的基础上,我们可以得到一些不同的排序算法。

LSD sort

一种最典型的方法就是从最低位向最高位的方式依次排序,这种和前面的radix sort的思路基本上完全一样。不过在前面的基础上针对字符的情况稍微做一点修改。详细的代码实现如下:

Java代码

1.public class LSD {

2.public static void sort(String[] a, int w) {

3.int n = a.length;

4.int R = 256;

5. String[] aux = new String[n];

6.for(int d = w - 1; d >= 0; d--) {

7.int[] count = new int[R + 1];

8.for(int i = 0; i < n; i++)

9. count[a[i].charAt(d) + 1]++;

10.

11.for(int r = 0; r < R; r++)

12. count[r + 1] += count[r];

13.

14.for(int i = 0; i < n; i++)

15. aux[count[a[i].charAt(d)]++] = a[i];

16.

17.for(int i = 0; i < n; i++)

18. a[i] = aux[i];

19. }

20. }

21.}

对于等长的字符串,而且里面字符的取值在一个比较小范围内时,这种LSD排序的方式比较理想。那么,如果我们把条件稍微放宽一点,如果字符串的长度其实不是等长的呢?有没有办法利用前面的计数排序呢?

MSD

和前面LSD不一样的就是,我们可以采用从最高位到最低位排序的方式来排序,同时,它可以处理数组长度不一致的情况。一般来说,当数组长度一致的时候,我们定义一个对应的数组来映射它所在的索引,如果不一致的时候就会出现当访问到某个位置的时候,其中某个字符串已经超出访问范围了。这时候该怎么办呢?

对于超出字符串访问范围的,我们可以定义一个charAt(i)的方法,超过范围的元素返回索引值-1。这样所有超出原来范围的数组都可以集中统计在-1的这个位置上。在详细映射实现的时候,我们可以将数组的长度加长一位。所有映射到索引位置的元素加一,这样-1位置的元素就相当于新数组里索引为0的位置。这样可以得到一个实现:

Java代码

1.public class MSD {

2.private static int R = 256;

3.private static final int M = 15;

4.private static String[] aux;

5.

6.private static int charAt(String s, int d) {

7.if(d < s.length()) return s.charAt(d);

8.else return -1;

9. }

10.

11.public static void sort(String[] a) {

12.int n = a.length;

13. aux = new String[n];

相关文档
最新文档