4

我是一名计算机科学专业的学生(刚刚开始),我正在使用伪代码编写一个随机枢轴版本的 Quicksort。我已经编写并测试了它,但是一切都很完美......

分区部分看起来有点太复杂了,感觉我漏掉了什么或者想多了。我不明白这是否可以,或者我是否犯了一些可以避免的错误。

长话短说:它有效,但如何做得更好?

提前感谢所有帮助

void partition(int a[],int start,int end)
{
    srand (time(NULL));
    int pivotpos = 3;   //start + rand() % (end-start);
    int i = start;    // index 1
    int j = end;      // index 2
    int flag = 1;
    int pivot = a[pivotpos];   // sets the pivot's value
    while(i<j && flag)      // main loop
    {
        flag = 0;
        while (a[i]<pivot)
        {
            i++;
        }
        while (a[j]>pivot)
        {
            j--;
        }
        if(a[i]>a[j]) // swap && sets new pivot, and restores the flag
        {
            swap(&a[i],&a[j]);
            if(pivotpos == i)
                pivotpos = j;
            else if(pivotpos == j)
                pivotpos = i;
            flag++;
        }
        else if(a[i] == a[j])       // avoids getting suck on a mirror of values (fx pivot on pos 3 of : 1-0-0-1-1)
        {
            if(pivotpos == i) 
                j--;
            else if(pivotpos == j)
                i++;
            else
            {
                i++;
                j--;
            }
            flag++;
        }
    }
}
4

2 回答 2

4

这是Introduction to Algorithmspartition()的伪代码,叫做Lomuto's Partitioning Algorithm,书下面有很好的解释。

PARTITION(A, p, r)
1 x ← A[r]
2 i ← p - 1
3 for j ← p to r - 1
4   do if A[j] ≤ x
5       then i ←i + 1
6           exchange A[i] ↔ A[j]
7 exchange A[i + 1] ↔ A[r]
8 return i +1

您可以根据上面的伪代码轻松实现随机分区实现。正如评论所指出的,srand()partition.

// srand(time(NULL));
int partition(int* arr, int start, int end)
{
    int pivot_index = start + rand() % (end - start + 1);
    int pivot = arr[pivot_index ];

    swap(&arr[pivot_index ], &arr[end]); // swap random pivot to end.
    pivot_index = end;
    int i = start -1;

    for(int j = start; j <= end - 1; j++)
    {
        if(arr[j] <= pivot)
        {
            i++;
            swap(&arr[i], &arr[j]);
        }
    }
    swap(&arr[i + 1], &arr[pivot_index]); // place the pivot to right place

    return i + 1;
}

书中还提到了另一种分区方法,叫做Hoare's Partitioning Algorithm,伪代码如下:

Hoare-Partition(A, p, r)
x = A[p]
i = p - 1
j = r + 1
while true
    repeat
        j = j - 1
    until A[j] <= x
    repeat
        i = i + 1
    until A[i] >= x
    if i < j
        swap( A[i], A[j] )
    else
        return j

划分后,A[p...j] 中的每个元素 ≤ A[j+1...r] 中的每个元素。所以快速排序将是:

QUICKSORT (A, p, r)
if p < r then
 q = Hoare-Partition(A, p, r)
 QUICKSORT(A, p, q)
 QUICKSORT(A, q+1, r)
于 2014-01-26T01:38:00.740 回答
3

有多种方法可以对快速排序进行分区,以下可能是我能想到的最简单的方法。通常使用两种划分学派:

  1. Squeeze - 折叠序列的两端,直到找到合适的交换,然后将两个元素交换到分区的正确边。实施起来并不简单,但可以比替代方案更有效(减少交换次数)......
  2. 扫描- 使用单个从左到右(或从右到左)扫描值,将值交换为随着算法运行而移动的递增枢轴索引。实现起来非常简单,如下所示。

我更喜欢用于学习快速排序和分区的人的 Sweep 算法,只是因为它实现起来非常简单。两者都可以实现就地分区,如下面的实现所示。除了在任何时候,swap()您都不会看到存储在 temp-storage 中的值。

使用随机枢轴选择只是其中的一小部分。下面展示了如何初始化随机数生成器,并演示了您可能会在其中找到的最简单的分区算法和快速排序用法。

除其他外,它演示了在 C/C++ 中,您不需要分区的两端,因为可以使用简单的指针算法来调整分区的“上”半部分。请参阅quicksort()函数以了解如何完成。

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

void swap(int *lhs, int *rhs)
{
    if (lhs == rhs)
        return;

    int tmp = *lhs;
    *lhs = *rhs;
    *rhs = tmp;
}

int partition(int ar[], int len)
{
    int i, pvt=0;

    // swap random slot selection to end.
    //  ar[len-1] will hold the pivot value.
    swap(ar + (rand() % len), ar+(len-1));
    for (i=0; i<len; ++i)
    {
        if (ar[i] < ar[len-1])
            swap(ar + i, ar + pvt++);
    }

    // swap the pivot value into position
    swap(ar+pvt, ar+(len-1));
    return pvt;
}

void quicksort(int ar[], int len)
{
    if (len < 2)
        return;

    int pvt = partition(ar, len);
    quicksort(ar, pvt++); // note increment. skips pivot slot
    quicksort(ar+pvt, len-pvt);
}


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

    const int N = 20;
    int data[N];

    for (int i=0; i<N; ++i)
    {
        data[i] = rand() % 50 + 1;
        printf("%d ", data[i]);
    }
    puts("");

    quicksort(data, N);

    for (int i=0; i<N; ++i)
        printf("%d ", data[i]);

    puts("");

    return 0;
}

输出(显然不同)

32 49 42 49 5 18 41 48 22 33 40 27 12 47 41 6 50 27 8 7 
5 6 7 8 12 18 22 27 27 32 33 40 41 41 42 47 48 49 49 50 

注意:这不考虑使用 的模偏差rand() % len,坦率地说,对于这个例子来说这样做是多余的。如果它很关键,我会完全使用另一个生成器。可以在此站点上的这篇文章中找到有关为快速排序分区选择随机枢轴位置的方法的出色讨论,其中包括指向不同方法的许多链接。我建议审查它。

于 2014-01-26T03:14:08.410 回答