使用O(m)空间?</int>在O(n)时间内对vector <int>(n)进行排序

时间:2013-10-30 03:04:41

标签: c++ algorithm sorting big-o time-complexity

我的vector<unsigned int> vec大小为nvec中的每个元素都在[0, m]范围内,没有重复项,我想对vec进行排序。如果允许使用O(m)空间,是否可以比O(n log n)时间更好?在一般情况下mn大得多,在最坏的情况下m == n

理想情况下,我想要一些O(n)。

我觉得有一种方法可以做到这一点:

  1. unsigned int aux[m];
  2. aux[vec[i]] = i;
  3. 以某种方式提取排列并置换vec
  4. 我被困在如何做3。

    在我的申请中,m大约为16k。但是,这种情况在内部循环中占据了我运行时的很大一部分。

3 个答案:

答案 0 :(得分:3)

基数排序,不要求对值小于m的限制。

下面的示例实现是在int类型上进行模板化的。如果你的m总是小于2 ^ 15你应该使用int16_t的向量(或者如果值总是正的则更好uint16_t以避免偏移来处理有符号整数)。这将只需要两个排序过程而不是4个32位整数。如果您无法更改输入类型,则可以使用特殊情况下的代码只执行两次传递并避免使用带符号的偏移量。

此实现为O(n)并使用O(n)额外空间(排序不到位)。

template<typename I>
void radix_sort(I first, I last) {
    using namespace std;
    typedef remove_reference_t<decltype(*first)> int_t;
    typedef make_unsigned<int_t>::type uint_t;

    const uint_t signedOffset = is_signed<int_t>::value ? uint_t(numeric_limits<int_t>::max()) + uint_t(1) : 0;
    auto getDigit = [=](uint_t n, int power) -> size_t { return ((n + signedOffset) >> (power * 8)) & 0xff; };

    array<size_t, 256> counts;
    vector<int_t> sorted(distance(first, last));
    for (int power = 0; power < sizeof(int_t); ++power) {
        counts.fill(0);
        for_each(first, last, [&](int_t i) { ++counts[getDigit(i, power)]; });
        partial_sum(begin(counts), end(counts), begin(counts));
        for_each(reverse_iterator<I>(last), reverse_iterator<I>(first), [&](int_t i) {
            sorted[--counts[getDigit(i, power)]] = i;
        });
        copy(begin(sorted), end(sorted), first);
    }
}

答案 1 :(得分:1)

如果您知道m = O(n 2 )的事实,您可以执行base-n基数排序来对数组进行排序。这就像一个普通的基数排序,但你没有2个桶或10个桶,而是有n个桶,每个桶的数字都是一个可能的数字。

由于基数b中基数排序的运行时为O(n log b U),其中U是最大值,在这种情况下,我们知道运行时为O(n log n n 2 )= O(n)。这比O(n log n)渐近地快。它也只需要O(n)存储器,这低于O(m)的限制。

希望这有帮助!

答案 2 :(得分:0)

不,你需要这样做:

unsigned int aux[m + 1];
bzero(aux, sizeof(aux));

foreach x in (vec) { aux[x]++; }

for(x = 0; x <= m; x++)
  for(qty = 0; qty < x; qty++)
    output(x);