找到无序数组的排序版本的N个连续元素的最佳方法是什么?

时间:2017-07-14 07:45:19

标签: c algorithm sorting

例如:我有一个10个元素的未排序列表A.我需要从ki的{​​{1}}个连续元素的子列表。

i+k-1

4 个答案:

答案 0 :(得分:2)

如果指定了ik,则可以使用特殊版本的快速排序,停止对i .. i+k范围之外的数组部分进行递归。如果可以修改数组,请执行此部分排序,如果无法修改数组,则需要进行复制。

以下是一个例子:

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

// Partial Quick Sort using Hoare's original partition scheme
void partial_quick_sort(int *a, int lo, int hi, int c, int d) {
    if (lo < d && hi > c && hi - lo > 1) {
        int x, pivot = a[lo];
        int i = lo - 1;
        int j = hi;

        for (;;) {
            while (a[++i] < pivot)
                continue;

            while (a[--j] > pivot)
                continue;

            if (i >= j)
                break;

            x = a[i];
            a[i] = a[j];
            a[j] = x;
        }
        partial_quick_sort(a, lo, j + 1, c, d);
        partial_quick_sort(a, j + 1, hi, c, d);
    }
}

void print_array(const char *msg, int a[], int count) {
    printf("%s: ", msg);
    for (int i = 0; i < count; i++) {
        printf("%d%c", a[i], " \n"[i == count - 1]);
    }
}

int int_cmp(const void *p1, const void *p2) {
    int i1 = *(const int *)p1;
    int i2 = *(const int *)p2;
    return (i1 > i2) - (i1 < i2);
}

#define MAX 1000000

int main(void) {
    int *a = malloc(MAX * sizeof(*a));
    clock_t t;
    int i, k;

    srand((unsigned int)time(NULL));

    for (i = 0; i < MAX; i++) {
        a[i] = rand();
    }
    i = 20;
    k = 10;
    printf("extracting %d elements at %d from %d total elements\n",
           k, i, MAX);
    t = clock();
    partial_quick_sort(a, 0, MAX, i, i + k);
    t = clock() - t;
    print_array("partial qsort", a + i, k);
    printf("elapsed time: %.3fms\n", t * 1000.0 / CLOCKS_PER_SEC);

    t = clock();
    qsort(a, MAX, sizeof *a, int_cmp);
    t = clock() - t;
    print_array("complete qsort", a + i, k);
    printf("elapsed time: %.3fms\n", t * 1000.0 / CLOCKS_PER_SEC);

    return 0;
}

使用包含100万个随机整数的数组运行此程序,从偏移量20开始提取排序数组的10个条目,得出以下结果:

extracting 10 elements at 20 from 1000000 total elements
partial qsort: 33269 38347 39390 45413 49479 50180 54389 55880 55927 62158
elapsed time: 3.408ms
complete qsort: 33269 38347 39390 45413 49479 50180 54389 55880 55927 62158
elapsed time: 149.101ms

与整个阵列的排序相比,它确实要快得多( 20x 50x ),即使只是选择了一个简单的枢轴。尝试多次运行,看看时间如何变化。

答案 1 :(得分:1)

您可以使用Quickselect或堆选择算法来获取i+k最小的项目。 Quickselect就地工作,但它会修改原始数组。如果项目列表大于内存中的项目列表,它也将无法工作。 Quickselect是O(n),但具有相当高的常数。当您选择的项目数量只占项目总数的一小部分时,堆选择算法会更快。

堆选择算法背后的想法是使用第一个i+k项初始化最大堆。然后,迭代其余项目。如果项目小于max-heap上的最大项目,则从max-heap中删除最大项目,并将其替换为新的较小项目。完成后,堆上有第一个i+k项,顶部有最大的k项。

代码非常简单:

heap = new max_heap();
Add first `i+k` items from a[] to heap
for all remaining items in a[]
    if item < heap.peek()
        heap.pop()
        heap.push(item)
    end-if
end-for

// at this point the smallest i+k items are on the heap

这需要O(i + k)额外内存,最坏情况下运行时间为O(n log(i + k))。当(i+k)小于n的约2%时,它通常会优于Quickselect。

有关此内容的更多信息,请参阅我的博文When theory meets practice

顺便说一下,您可以根据i稍微优化一下内存使用量。也就是说,如果数组中有十亿个项目并且您需要项目999,999,000到999,999,910,则上面的标准方法将需要一个巨大的堆。但是您可以将该问题重新转换为需要选择最后1,000个项目中最小的一个的问题。然后你的堆成为1,000个项目的最小堆。只需要一点点数学来确定哪种方式需要最小的堆。

当然,如果您想要600,000,000到600,000,010之类的物品,这并不会有多大帮助,因为您的堆中仍有4亿个物品。

但是,对我来说,如果时间不是很大的问题,你可以使用Floyd的算法就地在数组中构建堆,弹出第一个i项就像你对堆排序一样,下一个k项是你正在寻找的。这将需要恒定的额外空间和O(n +(i + k)* log(n))时间。

考虑到这一点,您可以使用一堆(i + k)项(如上所述)就地实现堆选择逻辑。实施它会有点棘手,但它不需要任何额外的空间,并且具有相同的运行时间O(n * log(i + k))。

请注意,两者都会修改原始数组。

答案 2 :(得分:0)

一个想法可能是扫描你的数组中更大或相等数量的i和更小或相等数量的i + k并将它们添加到另一个列表/容器中。

这将带你O(n)并给出你需要的无序数字列表。然后你对那个列表O(nlogn)进行排序,你就完成了。

对于非常大的数组,此方法的优点是您将对较小的数字列表进行排序。 (鉴于k相对较小)。

答案 3 :(得分:0)

您可以做的一件事就是修改heapsort,这样您就可以先创建堆,然后弹出第一个i元素。从堆中弹出的下一个k元素将是您的结果。丢弃剩余的n - i - k元素,让算法提前终止。

结果将显示在O((i + k) log n) O(n log n)中,但ik的相对较低值会显着提高。