字符串排序
1.低位优先的字符串排序(LSD)
低位优先的字符串排序是从右到左依次检查键中的字符,这种方法适用于键的长度都相同的字符串。
假设字符串的长度都为w,那么就从右向左按基数排序的方法排序w遍。
代码实现:
//低位优先排序
template <typename T>
void LSD(vector<T> &a, int w) //通过前w个字符对a排序
{
int N = a.size(); //N个字符串
int R = 256; //R个字符构成的字母表
vector<T> aux(N); //拷贝数组
for (int d = w - 1; d >= 0; d--)
{
vector<int> count(R + 1);
for (int i = 0; i < N; i++) //计算出现频率
count[a[i][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][d]]++] = a[i];
for (int i = 0; i < N; i++) //回写
a[i] = aux[i];
}
}
测试数据:
4PGC938
2IYE230
3CIO720
1ICK750
1OHV845
4JZY524
1ICK750
3CIO720
1OHV845
1OHV845
2RLA629
2RLA629
3ATW723
测试结果:
1ICK750
1ICK750
1OHV845
1OHV845
1OHV845
2IYE230
2RLA629
2RLA629
3ATW723
3CIO720
3CIO720
4JZY524
4PGC938
性能分析:
对于基于R个字符的字母表的N个长为W的字符串为键的元素,LSD需要访问~7WN+3WR次数组,使用的额外空间与N+R成正比。对于常用的应用,R会远小于N,因此LSD的总运行时间与WN成正比。
2.高位优先的字符串排序(MSD)
高位优先的字符串排序会从左到右,首先查看的是最高位的字符,将最高位的字符按照顺序排列之后,再将每个首字母对应的子数组排序。MSD是一种通用的字符串排序。
在MSD的实现中,我们会用插入排序对排序数组较小的时候进行优化,这样可以大大提高MSD的效率。
代码实现:
//高位优先排序
class MSD
{
private:
int R = 256; //基数
int M = 3; //小数组的切换阈值
vector<string> aux; //辅助数组
int charAt(string s, int d)
{
return (d < s.size()) ? s[d] : -1;
}
public:
MSD(vector<string> &a)
{
int N = a.size();
aux.resize(N);
sort(a, 0, N - 1, 0);
}
void sort(vector<string> &a, int lo, int hi, int d)
{
if (hi <= lo + M)
{
for (int i = lo; i <= hi; i++) //插入排序
{
string key = a[i];
int j = i - 1;
while (j >= 0 && key < a[j])
{
a[j + 1] = a[j];
j--;
}
a[j + 1] = key;
}
return;
}
vector<int> count(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);
}
};
测试数据:
she
sells
seashells
by
the
seashore
the
shells
she
sells
are
surely
seashells
测试结果:
are
by
seashells
seashells
seashore
sells
sells
she
she
shells
surely
the
the
性能分析:
对于大小为R的字母表的N个字符串排序,高位优先的字符串排序算法访问次数在8N+3R到~7wN+3wR,w为字符串的平均长度。所需的空间最坏情况下与RW成正比,W为最长字符串的长度。
3.三向字符串快速排序
三向字符串快速排序通过优化MSD,根据键的首字母进行三向切分(大于,等于和小于键),仅在中间子数组的下一个字符继续递归排序。
代码实现:
//三向字符串快速排序
class Quick3Srting
{
private:
int charAt(string s, int d)
{
return (d < s.size()) ? s[d] : -1;
}
public:
Quick3Srting(vector<string> &a)
{
sort(a, 0, a.size() - 1, 0);
}
void sort(vector<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)
swap(a[lt++], a[i++]);
else if (t > v)
swap(a[i], a[gt--]);
else
i++;
}
sort(a, lo, lt - 1, d);
if (v >= 0)
sort(a, lt, gt, d + 1);
sort(a, gt + 1, hi, d);
}
};