最快的方法是将重复值的整数数组部分排序到存储桶中

时间:2018-02-12 01:13:39

标签: c arrays algorithm sorting bucket

假设我有一个大的未排序的整数数组(C / C ++),它们主要重复一小部分值。例如,如果我从以下数组开始:

{ 0, 3, 3, 3, 0, 1, 1, 1, 3, 2, 2, 3, 0, 1, 1, 1, 2, 2, 2, 2, 0, 0, 1}

我想最终得到这个:

{ 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3}

实际上,我的数组将有数千个元素,但它们可以拥有的值范围仍然相对较小,就像十几个可能的值。

我的问题是传统的排序算法(qsort,mergesort等)看起来有点矫枉过正,因为他们会尝试确保每个元素都处于正确的位置。但我正在寻找一种算法,它只关心将元素分组为“桶”,并且知道一旦实现就终止。

2 个答案:

答案 0 :(得分:4)

使用地图:

map<int, unsigned> counts;
for (auto value: values)
    ++counts[value];

auto it = begin(values);
for (auto value_count : counts)
    while (value_count.second--)
        *it++ = value_count.first;

也就是说,创建一个有序的值到计数的映射,然后用它来覆盖(或创建其他地方)整个数组,并使用每个值的正确计数。

当然,如果值总是小范围内的整数,则可以使用数组而不是地图 - 例如,[0,3]中的值为:

array<unsigned, 4> counts = {};
for (auto value: values)
    ++counts[value];

答案 1 :(得分:4)

嗯,基于此:

  

未排序的整数数组,大多数重复一小部分值

假设列表中有一个最大值,您可以这样做:

#include <stdio.h>
#include <string.h>

int group_vals(int *arr, size_t len, int max)
{
    int count[max+1];
    memset(count, 0, sizeof count);


    for(size_t i = 0; i < len; ++i)
        count[arr[i]]++;

    size_t index = 0;
    for(size_t i = 0; i < max + 1; ++i)
    {
        for(size_t j = 0; j < count[i]; ++j)
            arr[index++] = i;
    }
}

int main(void)
{
    int arr[] = { 0, 3, 3, 3, 0, 1, 1, 1, 3, 2, 2, 3, 0, 1, 1, 1, 2, 2, 2, 2, 0, 0, 1};

    for(size_t i = 0; i < sizeof arr / sizeof *arr; ++i)
        printf("%d, ", arr[i]);
    puts("");

    group_vals(arr, sizeof arr / sizeof *arr, 3);

    for(size_t i = 0; i < sizeof arr / sizeof *arr; ++i)
        printf("%d, ", arr[i]);
    puts("");

    return 0;
}

这里我知道3是列表的最大值。这输出

0, 3, 3, 3, 0, 1, 1, 1, 3, 2, 2, 3, 0, 1, 1, 1, 2, 2, 2, 2, 0, 0, 1, 
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 1, 

修改

注意:正如用户coderredoc在评论中指出的那样,此方法的局限性 是它只在原始数组只包含正数时才有效。 改进它以处理负数不是一个大问题:

int group_vals(int *arr, size_t len, int absmax)
{
    int count[2*absmax+1];
    memset(count, 0, sizeof count);


    for(size_t i = 0; i < len; ++i)
    {
        int v = arr[i];
        size_t idx;

        if(v == 0)
            idx = absmax;
        else
            idx = absmax + v;

        count[idx]++;
    }

    size_t index = 0;
    for(size_t i = 0; i < 2*absmax + 1; ++i)
    {
        int v;
        if(i == absmax)
            v = 0;
            v = i - absmax;

        for(size_t j = 0; j < count[i]; ++j)
        {
            arr[index++] = v;
        }
    }
}

现在函数需要数组绝对值的最大值。

此版本打印:

-2, 0, 1, 3, 2, 3, -2, -1, -1, 3, 3, 
-2, -2, -1, -1, 0, 1, 2, 3, 3, 3, 3, 
PS:我没有看过John Zwinck的答案,但我们都有相同的想法,这就是 C版本。