Radix按字符串数组排序?

时间:2014-04-13 02:59:02

标签: c++ arrays sorting radix-sort

我一直在研究,虽然我已经想出了使用Radix Sort按字母顺序排列字符串数组的一般想法,但我知道我的方向错了。

这是我到目前为止所做的:

void radixSort(string* sortMe, int l)
{
    queue<string>* sections = new queue<string>[27];    //Have a-z, and also one for strings that are null terminated.
    for(int i = 0; i < numElements; i++)
    {
        if(!(sortMe[i][l] == 32))
            sections[sortMe[i][l]-96].push(sortMe[i]);      //-96 because the ascii code for a is 97. If, for example a is the character, it will be placed at 1. 0 is left for null characters
    }

    for(int i =0; i < 26; i++)
    {
        while(!sections[i].empty())
        {
            temp.push_back(sections[i].front());
            sections[i].pop();
        }
    }
}

到目前为止我用第一个字符对所有字符串进行排序,我知道然后我必须完成剩余字符的子数组并对它们进行排序,但是如何有效地实现呢?字符串大小可变,可以包含空格,例如:

  • 细分
  • 主要街道
  • 裤子
  • impaled decolonizing
  • 泥质
  • 轴向满意度
  • 喜怒无常
  • 过敏
  • hairbreadths
  • 面霜激增
  • unlaboured
  • 印第安纳
  • buggiest
  • 毛里塔尼亚
  • 射气
  • 赞歌
  • zouaves dishpan
  • 拖曳
  • solarisms
  • remunerativeness
  • 增溶
  • ooziness
  • toastier
  • 波特
  • 后缀
  • 无力的tiding
  • disassimilated
  • 喘气
  • flirtier

我发现这似乎是有用的: http://algs4.cs.princeton.edu/lectures/51DemoKeyIndexedCounting.pdf

2 个答案:

答案 0 :(得分:5)

你发现的幻灯片很棒!但是这些队列在哪里来自你的代码呢?

无论如何,你在这里(live example):

template <typename E>
size_t bin(const E& elem, size_t digit)
{
    return elem.size() > digit ? size_t(elem[digit]) + 1 : 0;
}

template <size_t R, typename C, typename P>
void radix_sort(P& pos, const C& data, size_t digit)
{
    using A = std::array<size_t, R + 1>;
    A count = {};
    P prev(pos);

    for (auto i : prev)
        ++count[bin(data[i], digit)];

    A done = {}, offset = {{0}};
    std::partial_sum(count.begin(), count.end() - 1, offset.begin() + 1);

    for (auto i : prev)
    {
        size_t b = bin(data[i], digit);
        pos[offset[b] + done[b]++] = i;
    }
}

struct shorter
{
    template <typename A>
    bool operator()(const A& a, const A& b) { return a.size() < b.size(); }
};

template <size_t R, typename C>
std::vector<size_t> radix_sort(const C& data)
{
    std::vector<size_t> pos(data.size());
    std::iota(pos.begin(), pos.end(), 0);

    size_t width = std::max_element(data.begin(), data.end(), shorter())->size();

    for (long digit = long(width) - 1; digit >= 0; --digit)
        radix_sort<R>(pos, data, size_t(digit));

    return pos;
}

你可以像那样使用

int main()
{
    std::vector<std::string> data = generate();
    std::vector<size_t> pos = radix_sort<128>(data);
    for (auto i : pos)
        std::cout << data[i] << std::endl;
}

其中generate()包含在实例中,并生成问题中给出的字符串。

我不是要解释这是如何工作的,我假设你可以弄清楚,因为你正在处理这个问题。但是有一些评论是有条不紊的。

  • 我们既没有对输入序列进行排序,也没有返回已排序的副本;我们只是在排序的序列中返回输入元素的位置序列。

  • 我们正在从右到左处理字符串。

  • 复杂度为O(lw),其中l是输入长度(输入字符串的数量),w是最大输入宽度(所有输入字符串的最大长度) 。因此,如果字符串宽度变化不大,则此算法有意义。

  • R的第一个模板参数radix_sort()是输入中每个数字(字母)的可能值的数量。例如。 R = 128表示可能的值为0..127。这应该适合您的输入。我没有尝试对ASCII代码做任何聪明的事情,但你可以自定义函数bin()

  • bin()的输出中,值0保留为表示“我们已超过此字符串的结尾”。这些字符串放在仍在继续的其他字符串之前。

  • 我试图为变量和函数提供不言自明的名称,并尽可能使用标准库调用来执行常见任务。

  • 代码是通用的,例如它可以对任何包含随机访问容器的随机访问容器进行排序,而不仅仅是字符串向量。

  • 我在这里和那里使用C ++ 11的功能是为了方便,但没有什么是必要的:只用C ++ 03就可以轻松做到这一点。

答案 1 :(得分:0)

非常类似于iavr,但排序到位(使用g ++ -O3对iavr解决方案进行基准测试,与iavr的1780ms相比需要大约2020ms),享受常规接口和可恢复代码< / strong>即可。 Iavr实现的问题在于它的逻辑仅适用于字符串容器,并且不易扩展到其他类型。显然他的专业版本效率更高,但是为了规律性而牺牲一些性能可能是值得的。 您可以在radix sort implementation

找到其余代码

General Radix sort:

template <typename T> 
using Iter_value = std::iterator_traits<T>::value_type;

// intermediate struct to get partial template specialization
template<typename Iter, typename T, size_t range = 256>
struct rdx_impl {
    static void rdx_sort(Iter begin, Iter end, int bits) { 
        // bits is # bits to consider up to if a max val is known ahead of time
        // most efficent (theoretically) when digits are base n, having lg(n) bits
        constexpr size_t digit_bits {8};        // # bits in digit, 8 works well for 32 and 64 bit vals

            size_t d {0};                   // current digit #
            for (long long mask = (1 << digit_bits) - 1;
                d * digit_bits < bits;) {// ex. 0x000000ff for setting lower 8 bits on 32 bit num
                cnt_sort(begin, end, range, Digit_cmp<T>(mask, digit_bits*d));
                ++d;
            }
        }
    };

// specialization of rdx_sort for strings
struct Shorter {
    template <typename Seq>
    bool operator()(const Seq& a, const Seq& b) { return a.size() < b.size(); }
};
template <typename Iter>    
struct rdx_impl<Iter, std::string> {    // enough to hold ASCII char range
    static void rdx_sort(Iter begin, Iter end, int) {
        // ignore additional int argument
        int len_max = std::max_element(begin, end, Shorter())->size();
        for (int d = len_max - 1; d >= 0; --d)
            cnt_sort(begin, end, 128, Digit_cmp<std::string>(d));
    }
};

// generic call interface for all iterators 
template <typename Iter>   // use intermediate struct for partial specialization
void rdx_sort(Iter begin, Iter end, int bits) {
    rdx_impl<Iter, Iter_value<Iter>>::rdx_sort(begin, end, bits);
}

计算排序以对每个数字进行排序(就地):

template <typename Iter, typename Op>
void cnt_sort(Iter begin, Iter end, size_t range, Op op) {
    using T = typename Iter::value_type;
    std::vector<int> counts(range);   // init to 0
    for (auto i = begin; i != end; ++i) // count # elems == i
        ++counts[op(*i)]; 
    for (size_t i = 1; i < range; ++i)
        counts[i] += counts[i-1];   // turn into # elems <= i
    std::vector<T> res(end - begin);
    for (auto j = end;;) {
        --j;
        res[--counts[op(*j)]] = *j;
        if (j == begin) break;
    }
    // ~18% of time is spent on copying
    std::copy(res.begin(), res.end(), begin);
}

提取数字值:

template <typename T>   // overload digit_cmp for non-integral types top provide radix sort with digits
class Digit_cmp {   // functor for comparing a "digit" (particular bits)
    const long long mask; // 0..63 bitfield to test against
    const size_t to_shift;
public:
    Digit_cmp(long long m, size_t ts) : mask{m}, to_shift{ts} {}
    // by default assumes integral, just shifts
    size_t operator()(T n) const {    // char assuming r = 8
        return (n >> to_shift) & mask; // shift then mask for unit digit
    }
};
// specialization for strings
template <>
class Digit_cmp<std::string> {
    const size_t digit;
public:
    Digit_cmp(size_t d) : digit{d} {}
    size_t operator()(const std::string& str) {
        // 0 indicates past the end of the string
        return str.size() > digit ? str[digit] : 0;
    }
};