字符串排序/基数&Trie

原创 2016年08月30日 17:29:11

常规O(nlogn)就不谈了。

基数排序

O(n*maxLen),maxLen为所有字符串里的最长长度。

思路

代码中的 beginCh 为 输入的字符串里ascii最小的可能字符 的上一个字符

为方便说明,这里先假设输入的字符都是大写字母,此时输入里的最小字符是A(ASCII为65), beginCh@ (ASCII为64)。

排序规则像排整数一样从最低位开始,不够的部分用 beginCh 补全,如

ACM
AC

排序时是使用 ACMAC@ 来计数,从 M@ 开始排一次,然后 CC ,再 AA

小坑

  1. beginCh 要用 @ 而非 A 的原因:用 A 的话就不能确保

    ABCA
    AB
    ABC

    这种数据的顺序,因为排的时候 ABCAABC 都被当作ABCA来看待了,由于基数排序调用了稳定排序,会出现排序结果跟输入的字符串顺序有关的情况。

  2. 千万小心 str.length()k 的比较,如果是直接 str.length() - k 的话会导致结果错误,因为 str.length()unsigned long 而非 signed long ,无符号数和有无符号运算时会将结果转为无符号数。

实现

#include <stdio.h>
#include <string>
#include <cmath>
#include <cstring>
#include <iostream>
using namespace std;

const int maxCh = 100;         //空格起为可打印字符
const char beginCh = ' ' - 1;  //-1是因为数据有空格的情况下 ac和ac空格 的排序结果是无法确保的

void rSort(string s1[], int n) {
    string *tmp = new string [n];
    int *cnt = new int [maxCh];
    int maxLen = 0;
    for (int i = 0; i < n; ++i) {
        maxLen = max(maxLen, (int)s1[i].length());
    }

    for (int k = maxLen - 1; k >= 0;  --k) {
        memset(cnt, 0, sizeof(int)*maxCh);
        for (int i = 0; i < n; ++i) {
            char ch = (int)s1[i].length() > k ? s1[i][k] : beginCh;
            cnt[ ch - beginCh] ++;
        }
        for (int i = 1; i < maxCh; ++i) {
            cnt[i] += cnt[i - 1];
        }
        for (int i = n - 1; i >= 0;  --i) {
            char ch = (int)s1[i].length() > k ? s1[i][k] : beginCh;
            tmp[ cnt[ch - beginCh] - 1] = s1[i];
            cnt[ch - beginCh]--;
        }
        for (int i = 0; i < n; ++i) {
            s1[i] = tmp[i];
        }
    }
    delete[] tmp; delete[] cnt;
}

int main() {
    freopen("in.txt","r",stdin);
    // freopen("out.txt","w",stdout);
    string s1[1234];
    int n = 0;
    while (cin >> s1[n])n++;

    rSort(s1, n);
    for (int i = 0; i < n; ++i) {
        cout << s1[i] << endl;
    }
    return 0;
}

Trie树排序

优点还是Trie树的优点:省空间~~,不过新建结点时new操作不少,速度稍慢。
插入建树时O(n*len),排序时需要深搜一次O(m):m为树的结点数。

Trie树 稍作修改即可实现排序,每个结点存根结点到当前所组成的字符串,并且加个标记位表示是不是字符串结尾,这样的话深搜一次即可按顺序访问所有结点。

因为挺简单的,直接上代码

//图片的Graphviz代码
digraph G{
    " "->"a"[label = "a"]
    "a"->"ab"[label = "b"]
    "ab"->"abd"[label = "d"]
    "ab"->"abg"[label = "g"]
    "abg"->"abgh"[label = "h"]

    " "->"b"[label = "b"]
    "b"->"bc"[label = "c"]
}

实现

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

class TrieSort {

public:
    void init() {
        rt = new node();
    }
    void insert(string str) {
        insert(rt, str);
    }
    void sort(string strArray[]) {
        int sCnt = 0;
        sort(rt, strArray, sCnt);
    }
    //按序输出,不改变原数据
    void printSorted() {
        printSorted(rt);
    }

private:
    enum {charestNum = 100,beginCh = ' '};
    struct node
    {
        node *next[charestNum + 1];
        string cur;
        int cnt;
        node() {
            cur = "";
            memset(next,0,sizeof(next));
            cnt = 0;
        }
    };
    node *rt;

    void insert(node *p, string s1) {
        for (int i = 0; i < s1.length(); ++i) {
            int k = s1[i] - beginCh;
            if (!p->next[k])
                p->next[k] = new node();
            p->next[k]->cur = p->cur + s1[i];
            p = p->next[k];
        }
        p->cnt ++;
    }
    void printSorted(node *p) {
        while (p->cnt > 0) {cout << p->cur << endl; p->cnt--;}
        for (int i = 0; i < charestNum; ++i) {
            if (p->next[i]) {
                printSorted(p->next[i]);
            }
        }
    }
    void sort(node *p, string ans[], int& sCnt) {
        while (p->cnt > 0) {ans[sCnt++] = p->cur ; p->cnt--;}
        for (int i = 0; i < charestNum; ++i) {
            if (p->next[i]) {
                sort(p->next[i], ans, sCnt);
            }
        }
    }
} trie;
int main()
{
    freopen("in.txt", "r", stdin);
    // freopen("out.txt","w",stdout);

    string s1[200];
    int n = 0;
    trie.init();
    while (cin >> s1[n]) {trie.insert(s1[n]); n++;}
    // trie.printSorted();

    trie.sort(s1);
    for (int i = 0; i < n; ++i) {
        cout << s1[i] << endl;
    }
    return 0;
}

用来对拍的库排序

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
#define RE freopen("in.txt","r",stdin);
#define WE freopen("out.txt","w",stdout);

int main() {
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    string s1[100];
    int n = 0;
    while (cin >> s1[n])n++;
    sort(s1, s1 + n);
    for (int i = 0; i < n; ++i) {
        cout << s1[i] << endl;
    }
    return 0;
}

数据生成

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

//生成n行可打印字符
int main() {
    int i, a, b, n, k;
    freopen("in.txt", "w", stdout);
    srand((int)time(NULL));
    a = 1;
    b = 60;

    for (i = 0; i < 20; i++) {  //数据组数
        n = a + rand() % (b - a); //长度
        // printf("%d\n", n);
        for (int j = 0; j < n; ++j) {
            printf("%c", 33 + rand() % 92);
        }
        printf("\n");
    }
    return 0;
}

相关文章推荐

算法入门--基数排序(对长度为3的字符串排序)

#include #include /*利用基数排序对长度为3的字符串进行排序,稳定排序*/ void sort_at_i(char **a,int i,int length) { /*i为当前要...

自己理解三叉树TernarySearchTrie

花了差不多一天半的时间终于把一颗三叉树看完了,不过对于里面还有点疑惑,下面在代码里注释上了自己的理解,里面还存在一些疑问,欢迎理解的朋友们指出其中的错误,以及解答里面的疑问。package org.a...

数字字符串的基数排序

#include/** * created by:shishuai * QQ:641542616 * 函数功能:对多组相同位数的数字字符数组进行排序 * 函数实现方法:基数排序 * 时间复杂度:O(n...

字符串排序新探索——使用基数排序

一个偶然的机会发现字符串排序也可以使用基数排序来实现,而且是一个很有意思的问题,因为这其中有一个渐进优化的过程,本文先考虑字符串排序的几种实现方法,然后从理论上分析使用基数排序的复杂度,最后将其与快排...

POJ-3630-Phone List-Trie树(键树、数字查找树、基数树)

先看下键树的有关基础知识。 键树中的每个结点不是包含一个关键字,而是只包含组成关键字的符号。例如,若关键字是数值,则结点中只包含一个数位;若关键字是单词,则结点中只包含一个字母字符。这种树会给某种类...
  • lihao21
  • lihao21
  • 2011年02月06日 15:49
  • 1639

字典树/trie树,基数树/radix树

转自:百度百科:http://baike.baidu.com/view/2759664.htm

基数排序法by队列

  • 2014年11月16日 23:32
  • 4KB
  • 下载

【每日算法】计数&基数&桶&位图排序-简介

在前面的文章中,我们介绍的都是基于比较的排序。对于比较排序,对含n个元素的序列进行排序,在最坏情况下都要用O(n logn)次比较(归并排序和堆排序是渐近最优的)。本文将继续介绍以线性时间运行的排序算...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:字符串排序/基数&Trie
举报原因:
原因补充:

(最多只允许输入30个字)