【问题标题】:An algorithm to sort an array in linear time一种在线性时间内对数组进行排序的算法
【发布时间】:2018-02-08 00:31:06
【问题描述】:

我正在阅读 Skiena 的算法设计手册,但无法解决这个问题。

假设数组 A 由 n 个元素组成,每个元素是红色、白色或蓝色。我们 寻求对元素进行排序,以使所有红色排在所有白色之前 在所有的忧郁之前唯一允许的键操作是

Examine(A,i) { report the color of the ith element of A.
Swap(A,i,j) { swap the ith element of A with the jth element.

找到一个正确有效的红白蓝排序算法。有一个线性时间 解决方案。

我尝试使用快速排序,在 3 个枢轴上,我应该能够解决它,但是当我在快速排序中看到重复时,我不知道该怎么办。

【问题讨论】:

  • 那么我该如何处理呢?现在我正式没有办法。顺便说一句,这不是怎么回事,我只是在为面试做准备。
  • 每种颜色的计数 O(n);修复索引;相应地定位颜色 O(n)。
  • 还有 google 的荷兰国旗算法。
  • @DoSparKot 请仔细阅读问题。我只能使用交换和检查。
  • 你为什么沉迷于使用快速排序?没必要!

标签: algorithm language-agnostic


【解决方案1】:

维护两个指针:一个最初指向 0 的红色指针和一个指向数组最后一个元素的蓝色指针。

现在使用Examine 函数从左到右扫描数组。

  • 每次遇到红色元素时,将其与当前红色指针交换(使用Swap 函数)并递增红色指针。
  • 同样,每次遇到蓝色元素时,将其与当前蓝色指针交换,并递减蓝色指针。
  • 遇到白色元素时增加当前指针。
  • 当当前指针越过蓝色指针时停止。

现在数组应该按照你的意愿排序了。

这是Dutch National Flag Problem

【讨论】:

  • “如果执行了(蓝色)交换,请不要增加当前指针”。
  • @WillNess 如果你在红色交换的情况下也不增加当前指针,它会起作用,只是效率会低一点。
  • @AbhinavSarkar 可以给你一个小例子。我不明白这将如何工作。谢谢!
  • (由于某种原因错过了整个句子)。另一个问题是,我们是否被允许使用这三个额外的变量来保存索引还不清楚。
  • @WillNess 是的,您可以使用 3 个变量。
【解决方案2】:

这其实是 Dijkstra 提出的一个非常有名的问题,被称为Dutch national flag problem

上面链接的维基百科对如何解决这个问题和其他类似问题给出了相当不错的说明。

也可以应用 3 向快速排序来解决此问题。 This presentation 应该会给你一个很好的想法(相关内容从第 37 页开始)。此外,它确实在 O(n) 中工作,因为不同键的数量是一个常数,3(如第 43 页所述)。

【讨论】:

  • 我可以使用快速排序吗?
  • @user2117772 来自快速排序的一个分区传递就可以了。但是您需要具有多个枢轴区域的品种,而不仅仅是一个枢轴:最终您将拥有“较小的”,然后是“较大的”,然后是“等于枢轴的那些”。当然,不需要将支点最终交换到中间。
  • @user2117772:是的; O(n)。我已经编辑了我的答案以反映它。我希望它能回答你的问题。
  • 以下解释了三向快速排序方法以及完整的代码 - algs4.cs.princeton.edu/23quicksort
【解决方案3】:

这并不是一个真正的排序问题;这是一个分组问题。

遍历列表,计算红色、白色和蓝色元素的数量。现在您知道了解决方案的确切结构,例如:

XXXXYYYYZZZZZZZZZ

X 为红色,Y 为白色,Z 为蓝色。

现在创建三个指针:一个在 X 开头的位置,一个在 Y 的开头,一个在 A 中 Z 的开头。

推进每个指针,直到它在它的集合中的第一个元素是错误的。例如,如果这是 A:

XYXXYYXYZZZZZZZZZ

我们会将 X 指针 I 推进到第二个位置(索引 1),因为该元素不合适。

如果你对每个指针都这样做,你就会知道三个指针中的每一个都指向一个不合适的元素。然后遍历列表。每当您发现一个元素不合适时,将其与其对应的指针交换,即如果您在 Y 中找到一个 X,则将其与其 Y 指针交换,然后递增该指针 (Y) 直到它指向不合适的位置再次。

继续直到数组排序。

由于您遍历列表一次(n 个操作)以获取结构,然后每个指针最多将遍历列表一次(4 个指针 → 4n),因此您的总最大运行时间将为 5n,即 O(n) .

【讨论】:

  • 我可以使用快速排序吗?
  • 快速排序是 n log n 运行时间,所以这将打破你的 n 运行时约束。
  • 我觉得快速排序将是 O(n),因为我将扫描数组一次,枢轴发生变化,使得枢轴小于严格小于,右侧严格大于或等于。我使用快速排序手动完成,但我无法理解为什么它会是 nlogn,而不是 n。
  • 如果你知道枢轴(你知道),快速排序应该和这个算法一样。但前提是您执行两次,并使用 X、Y 和 Z 之间的边界位置作为枢轴。一般来说,这仍然需要比 O(n) 更长的时间
  • 两遍 O(n) 仍然是 O(n)。
【解决方案4】:

一个非常简单的线性时间算法是:

  1. 在数组中循环一次,找到红色、白色和蓝色的计数为 rcount、wcount、bcount。

  2. 有三个计数器,从 0 开始,rcount,(rcount + wcount)。称它们为 rcounter、wcounter 和 bcounter。

  3. 对于每个计数器,递增直到获得不适合该范围的颜色

  4. 从 0 开始,每当遇到一种颜色: 一个。如果颜色为红色且(计数器

当循环结束时,你就有了你的数组。

【讨论】:

  • 我只有交换和检查作为我的方法
  • 啊,没有增量操作?至少必须有一个 goto next ,对吧?
  • 没有。严格审查和交换
  • 我会说增量是隐含的。检查 i 然后检查 i+1 导致增量。循环比较也是如此。而且很可能证明,没有增量或循环就无法完成。
  • 如果没有循环或增量,您将受限于我所期望的恒定时间算法。
【解决方案5】:

我浏览了 Skiena 的书,也看到了这个问题 这是我的解决方案。

#include <stdio.h>

//swap the ith element of A with the jth element.
void swap(char arr[], int i, int j) {
    char temp;
    temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
    return;
}

int partition_color(char arr[], int low, int high, char color)
{
    int i;          // counter
    char p;          // pivot element
    int firsthing; // divider position for pivot

    p = color;          // choose the pivot element
    firsthing = high; // divider position for pivot element

    for (i = low; i < firsthing; i++) {
        if (arr[i] == color) {
            swap(arr, i, firsthing);
            firsthing--;
        }
    }

    return(firsthing);
}

void red_white_blue_sorting(char arr[], int n) {
    int pos;
    pos = partition_color(arr, 0, n, 'b');
    partition_color(arr, 0, pos, 'w');
    return;
}

int main() {

    char arr[] = {'r', 'b', 'r', 'w', 'b', 'w', 'b', 'r', 'r'};
    int n = sizeof(arr) / sizeof(arr[0]);

    red_white_blue_sorting(arr, n);
    for (int i = 0; i < n; i++)
        printf("%c ", arr[i]);

    printf("\n");
    return(0);
}

【讨论】:

  • 你能提供更多关于你提供的解决方案的信息吗?
【解决方案6】:

我在浏览 Skiena 的书时看到了这个问题。

我得到了一个 O(2n) 的解决方案:

假设 A = [B,R,W,R,W,B]

  1. 首先遍历数组并以 B 为轴进行分区,这样,所有 R 或 W 的值都将被推到数组的前面。

现在 A 将是; ['R', 'W', 'R', 'W', 'B', 'B'] => O(n)

  1. 再次通过 A,以 W 为轴心,这样所有 R 的值都将被推到数组的前面。我们可以忽略 B,因为它们已经到位。

结果:['R', 'R', 'W', 'W', 'B', 'B'] => O(2n)

这是一个线性解。

这是正确的方法吗?

【讨论】:

  • 您不应该使用写下您的问题作为答案。更喜欢写评论。
猜你喜欢
  • 1970-01-01
  • 2014-03-27
  • 2017-06-30
  • 2023-01-27
  • 2017-07-16
  • 2020-05-02
  • 1970-01-01
  • 2016-05-01
  • 2018-12-17
相关资源
最近更新 更多