字符串(1)——字符串排序

来源:互联网 发布:dbx260软件中文版 编辑:程序博客网 时间:2024/05/21 15:49

字符串排序

无数的重要领域都是基于字符串处理的。本章将会学习一些经典的字符串处理算法。

我们将学习两类不同的字符串排序算法。
1)从右到左检查键中的字符。这种方法一般被称为低位优先,适用于键的长度都相同的字符串排序。
2)从左到右检查键中的字符,这种方法一般被称为高位优先,和快排类似,通过递归来快速切分。


键索引计数法

做为两种排序方法的基础,我们需要用到键索引计数法。
他的作用是通过键的出现频率构造索引,然后用过索引排序数组。
具体分为四部:
1)频率统计:count数组计算每个键出现的频率,存储到count[r+1]
2)将频率转换为索引:从前往后count[r+1]都加上count[r],构造出索引的位置
3)数据分类:将元素复制到辅助数组aux[]中进行排序
4)回写:将排序后的元素复制回原数组
代码:
int N = a.size();vector<string> aux(N);vector<int> count(R + 1,0);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];

低位优先的字符串排序

低位优先的字符串排序算法能够稳定地将定长字符串排序。
缺点就是字符串长度必须是固定的。
具体步骤就是,从右往左对字符串的每一个字符进行键索引计数法排序。
代码:
#ifndef LSD_H#define LSD_H#include<string>using std::string;#include<vector>using std::vector;class LSD{public:void sort(vector<string> &a, int w)//w为字符串长度{int N = a.size();int R = 256;//字符的数量vector<string> aux(N);for (int d = w - 1; d >= 0; d--){vector<int> count(R + 1,0);for (int i = 0; i < N; i++)//计算出现频率count[a[i].at(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].at(d)]++] = a[i];for (int i = 0; i < N; i++)//回写a[i] = aux[i];}}};#endif

测试用例:
#include<iostream>#include<fstream>#include"LSD.h"using namespace std;int main(){vector<string> a;string aa;while (cin >> aa)a.push_back(aa);LSD s;s.sort(a,a[0].size());for (auto w : a)cout << w << endl;system("pause");return 0;}


高位优先的字符串排序

要实现一个通用字符串排序算法(字符串长度不一定相等),我们应该考虑从左向右遍历所有的字符。这种思想的一个很自然的方法就是一种递归算法,即高位优先的字符串排序。
具体方法是:将字符串从左往右开始对字符进行键索引计数法排序,然后递归对每个索引频率内的数据单独进行下一位字符排序。(类似于快排)
因为字符串长度是不同的,所以我们应该注意在计算频率时将长度小于所查字符的单独放一栏。所以我们需要对at()函数进行重构,当没有所查字符时返回-1,而键索引计数法也需要将查找的位置后移一位。
代码
#ifndef MSD_H#define MSD_H#include<string>using std::string;#include<vector>using std::vector;class MSD{private:int R = 256;//基数int M = 15;//小数组的切换阙值vector<string> aux;int at(string s, int d){ if (d < s.size())return s.at(d); else return -1; }public:void sort(vector<string> &a){int N = a.size();aux=vector<string>(N);sort(a, 0, N - 1, 0);}void sort(vector<string> &a, int lo, int hi, int d){//以d个字符为键将a[lo]至a[hi]排序if (hi <= lo)return;//if(hi<=lo+M){insertsort(a,lo,hi,d);return;}vector<int> count(R + 2);for (int i = lo; i <= hi; i++)//计算频率count[at(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[at(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);}};#endif

测试用例
#include<iostream>#include<fstream>#include"MSD.h"using namespace std;int main(){vector<string> a;string aa;while (cin >> aa)a.push_back(aa);MSD s;s.sort(a);for (auto w : a)cout << w << endl;system("pause");return 0;}



三向字符串快速排序

与三向快排相类似,将字符串以首字符进行三切分快排,然后递归对切分出来的同类子集进行次一字符的快排,直到元素字符排空为止。
代码:
#ifndef QUICK3STRING_H#define QUICK3STRING_H#include<string>using std::string;#include<vector>using std::vector;class Quick3string{private:int at(string s, int d){if (d < s.size())return s.at(d);else return -1; }void eaxh(vector<string> &a, int i,int j){string t = a[i];a[i] = a[j];a[j] = t;}public:void sort(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 = at(a[lo], d);//提取切分的字符int i = lo + 1;//遍历指针while (i <= gt){int t = at(a[i], d);//查看的字符if (t < v)eaxh(a, lt++, i++);else if (t > v)eaxh(a, i, gt--);else i++;}sort(a, lo, lt - 1, d);if (v >= 0)sort(a, lt, gt, d + 1);sort(a, gt + 1, hi, d);}};#endif

测试用例:
#include<iostream>#include<fstream>#include"Quick3string.h"using namespace std;int main(){vector<string> a;string aa;while (cin >> aa)a.push_back(aa);Quick3string s;s.sort(a);for (auto w : a)cout << w << endl;system("pause");return 0;}

算法性能对比

字符串排序算法的性能特点:算法稳定性原地排序运行时间额外时间优势邻域插入排序是是N~N21小数组或是已经有序的数组快速排序否是Nlog2NlogN适用于空间不足的情况三向快速排序是否N~NlogNlogN大量重复键低位优先是否NwN较短的定长字符串高位优先是否N~NwN+WR随机字符串三向字符串否是N~NwW+logN含有较长公共前缀归并排序是否Nlog2NN稳定的通用排序