算法学习笔记(八)——字符串排序


字符串排序

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);
    }
};


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM