一种在线性时间内对数组进行排序的算法

时间:2013-03-03 14:58:05

标签: algorithm language-agnostic

我正在阅读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.

找到一种正确有效的红白蓝分选算法。有一个线性时间 溶液

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

6 个答案:

答案 0 :(得分:2)

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

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

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

现在应该根据需要对数组进行排序。

这是Dutch National Flag Problem

答案 1 :(得分:1)

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

上面链接的维基百科给出了一个很好的解释如何解决这个问题和其他类似的问题。

也可以采用3向快速排序方式。 This presentation应该为您提供一个非常好的建议(相关内容从第37页开始)。此外,它在O(n)中工作,因为不同键的数量是常数,3(如第43页所述)。

答案 2 :(得分:0)

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

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

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

  3. 对于每个计数器,递增直到您得到的颜色不是该范围的正确颜色

  4. 从0开始,每当遇到颜色时: 一个。如果颜色为红色且(计数器< rcount),则增加rcount并继续(同样为白色和蓝色,具体取决于范围) 湾如果颜色是白色,则用wcounter和wcounter ++交换 C。如果颜色是蓝色,则用bcounter和bcounter ++交换

  5. 当循环结束时,你有阵列。

答案 3 :(得分:0)

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

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

XXXXYYYYZZZZZZZZZ

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

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

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

XYXXYYXYZZZZZZZZZ

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

如果对每个指针执行此操作,则您知道三个指针中的每一个都指向一个不合适的元素。然后遍历列表。每当你发现一个元素不合适时,用相应的指针交换它,即如果你在Ys中找到一个X,用它的Y指针交换它,然后递增该指针(Y),直到它指向一个不合适的地方试。

继续,直到对数组进行排序。

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

答案 4 :(得分:0)

我已经阅读了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);
}

答案 5 :(得分:-1)

我正在阅读Skiena的书并看到了这个问题。

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

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

  1. 首先遍历数组并分区旋转B,这样,所有R或W的值都将被推到数组的前面。
  2. 现在A将是; ['R','W','R','W','B','B'] =&gt;为O(n)

    1. 再次通过A,在W上旋转,以便将R的所有值推送到数组的前面。我们可以忽略B,因为它们已经存在。
    2. 结果:['R','R','W','W','B','B'] =&gt; O(2N)

      这是线性解决方案。

      这是正确的方法吗?