使用C

时间:2016-04-10 08:50:06

标签: c arrays algorithm sorting

我需要将k(1< = k< = 16)排序的数组合并为一个排序的数组。这是用于家庭作业,教授要求使用O(n)算法完成。合并2个数组没问题,我可以使用O(n)算法轻松完成。我觉得我的教授所要求的是对于具有O(n)算法的n个数组是可撤销的。

我使用以下算法拆分数组索引并在每个分区上运行InsertionSort。我可以将这些开始和结束索引保存到2D数组中。我只是没有看到如何使用O(n)进行合并,因为这将需要多个循环。如果有可能,是否有人有任何提示。我不是在寻找实际的代码,只是提示我应该从哪里开始/

int chunkSize = round(float(arraySize) / numThreads);
for (int i = 0; i < numThreads; i++) {
    int start = i * chunkSize;
    int end = start + chunkSize - 1;
    if (i == numThreads - 1) {
        end = arraySize - 1;
    }

    InsertionSort(&array[start], end - start + 1);
}

编辑:要求是算法为O(n),其中n是数组中元素的数量。另外,我需要在不使用最小堆的情况下解决这个问题。

编辑#2:这是我提出的算法。这里的问题是我没有将每次迭代的结果存储回原始数组中。我可以将它全部复制回来进行循环,但这样会很昂贵。除了使用memcpy之外,我有什么方法可以做到这一点吗?在下面的代码中,indices是一个2D数组[numThreads] [2],其中array [i] [0]是起始索引,array [i] [1]是第i个数组的结束索引。 / p>

void mergeArrays(int array[], int indices[][2], int threads, int result[]) {
    for (int i = 0; i < threads - 1; i++) {
        int resPos = 0;
        int lhsPos = 0;
        int lhsEnd = indices[i][1];
        int rhsPos = indices[i+1][0];
        int rhsEnd = indices[i+1][1];

        while (lhsPos <= lhsEnd && rhsPos <= rhsEnd) {
            if (array[lhsPos] <= array[rhsPos]) {
                result[resPos] = array[lhsPos];
                lhsPos++;
            } else {
                result[resPos] = array[rhsPos];
                rhsPos++;
            }
            resPos++;
        }

        while (lhsPos <= lhsEnd) {
            result[resPos] = array[lhsPos];
            lhsPos++;
            resPos++;
        }

        while (rhsPos <= rhsEnd) {
            result[resPos] = array[rhsPos];
            rhsPos++;
            resPos++;
        }
    }
}

4 个答案:

答案 0 :(得分:4)

您可以使用O(N*log(K))算法将一个已排序数组中的 K 排序数组合并,使用带有K个条目的优先级队列,其中 N 是元素的总数所有阵列。

如果K被视为为常量值(在您的情况下受限于16),则复杂度为O(N)

再次注意: N 是我帖子中的元素数量,而不是数组的数量。 在O(K)中合并数组是不可能的 - 简单复制需要O(N)

答案 1 :(得分:2)

使用您提供的事实:  (1)n是要合并的数组的数量;  (2)要合并的数组已经排序;  (3)合并需要是n阶,即数组的数量是线性的     (并且不是每个数组中元素数量的线性,因为您可能会错误地认为是第一眼)。

使用合并4个分类堆叠的卡片的类比,从低到高,面朝上。您将从其中一个桩中挑选具有最低面值的卡并将其(面朝下)放在合并的甲板上,直到所有桩都用完为止。
对于您的程序:为每个数组保留一个计数器,以获取已传输到输出的元素数。这同时是每个数组中下一个元素的索引未在输出中合并。选择您在其中一个位置找到的最小元素。您必须在所有数组中查找第一个等待元素,因此它的顺序为n。

此外,我不明白为什么MoB的答案得到了投票,但它没有回答这个问题。

答案 2 :(得分:0)

这是一种方法(伪代码)

input array[k][n]
init indices[k] = { 0, 0, 0, ... }
init queue = { empty priority queue }

for i in 0..k:
    insert i into queue with priority (array[i][0])

while queue is not empty:
    let x = pop queue
    output array[x, indices[x]]
    increment indices[x]
    insert x into queue with priority (array[x][indices[x]])

这可以在C中进一步简化。你必须找到一个合适的队列实现来使用,因为libc中没有。

此操作的复杂性:

&#34;当队列不为空时&#34; =&GT;为O(n)

&#34;将x插入队列......&#34; =&GT; O(log k)

=&GT; O(n log k)

如果考虑k =常数,则为O(n)。

答案 3 :(得分:0)

在对k个子数组进行排序后(该方法无关紧要),代码执行k-way合并。最简单的实现是k-1比较以确定每个k数组的最小前导元素,然后将该元素从其子数组移动到输出数组并从该数组中获取下一个元素。当到达数组的末尾时,算法下降到(k-1)路合并,然后(k-2)路合并,最后只剩下一个子数组并将其复制。这将是O(n)时间,因为k-1是常数。

可以通过使用最小堆(这是一些优先级队列的实现方式)来加速k-1比较,但它仍然是O(n),只有一个较小的常量。堆需要在开始时初始化,然后每次删除元素并添加新元素时更新。