5.1 字符串排序
本节我们将学习两类完全不同的字符串排序方法。
第一种方法会从右到左检查键中的字符。这种方法一般被称为低位优先(Least-Significant-Digit First,LSD)的字符串排序。使用数字(digit)代替字符(character)的原因要追溯到相同方法在各种数字类型中的应用。如果将一个字符串看作一个256进制的数字,那么从右向左检查字符串就等价于先检查数字的最低位。这种方法最适合用于键的长度都相同的字符串排序应用。
第二种方法会从左到右检查键中的字符,首先查看的是最高位的字符。这些方法通常称为高位优先(MSD)的字符串排序——本节将会学习两种此类算法。高位优先的字符串排序的吸引人之处在于,它们不一定需要检查所有的输入就能够完成排序。
高位优先的字符串排序和快速排序类似,因为它们都会将需要排序的数组切分为独立的部分并递归地用相同的方法处理子数组来完成排序。它们的区别之处在于高位优先的字符串排序算法在切分时仅使用键的第一个字符,而快速排序的比较则会涉及键的全部。要学习的第一种方法会为每个字符创建一个切分,第二种方法则总会产生三个切分,分别对应被搜索键的第一个字符小于、等于或大于切分键的第一个字符的情况。在分析字符串排序算法时,字母表的大小是一个重要的因素。尽管我们的重点是基于扩展的ASCII字符集的字符串(R=256),但也会分析来自较小字母表的字符串和来自较大字母表的字符串。
5.1.1 键索引计数法
作为热身,我们先学习一种适用于小整数键的简单排序方法。这种叫做键索引计数的方法本身就很实用,同时也是本节中将要学习的三种字符串排序算法中两种的基础。
5.1.2 低位优先的字符串排序
我们学习的第一个字符串排序算法叫做低位优先(LSD)的字符串排序。考虑以下应用:假设有一位工程师架设了一个设备来记录给定时间段内某条忙碌的高速公路上所有车辆的车牌号,他希望知道总共有多少辆不同的车辆经过了这段高速公路。
输入 | 排序结果 |
4PGC938 | 1ICK750 |
2IYE230 | 1ICK750 |
3CI0720 | 10HV845 |
1ICK750 | 10HV845 |
10HV845 | 10HV845 |
4JZY524 | 2IYE230 |
1ICK750 | 2RLA629 |
3CI0720 | 2RLA629 |
10HV845 | 3ATW723 |
10HV845 | 3CI0720 |
2RLA629 | 3CI0720 |
2RLA629 | 4JZY524 |
3ATW723 | 4PGC938 |
图5.1.2 适于使用低位优先的字符串排序算法的典型情况
将此类字符串排序可以通过键索引计数法来完成。如果字符串的长度均为W,那就从右向左以每个位置的字符作为键,用键索引计数法将字符串排序W遍。
int N = a.length;
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[].key()为[0,R)之间的一个整数)
算法5.1 低位优先的字符串排序
public class LSD
{
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];
}
}
}
要将每个元素均为含有W个字符的字符串数组a[]排序:从右向左,以每个位置的字符为键排序一次。