C随机枢轴快速排序(改进分区功能)

时间:2014-01-26 01:13:49

标签: c arrays quicksort partition

我是一名计算机科学专业的学生(刚刚开始),我正在从伪代码编写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++;
        }
    }
}

2 个答案:

答案 0 :(得分:4)

这是来自Introduction to Algorithmspartition()的伪代码,称为Lomuto的分区算法,本书下面有一个很好的解释。

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的分区算法,伪代码如下:

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)

答案 1 :(得分:3)

有多种方法可以对快速排序进行分区,以下内容可能是最简单的方法。通常使用两个分区学校:

  1. Squeeze - 折叠序列的两端,直到找到合适的交换,然后将两个元素交换到分区的正确边。实现起来并非易事,但可以比替代方案更有效(减少交换计数)......
  2. 扫描 - 使用单个从左到右(或从右到左)扫描值,将值交换为递增的枢轴索引,该索引在算法运行时移动。实现起来非常简单,如下所示。
  3. 我更喜欢Sweep算法,因为人们学习快速排序和分区只是因为它实现起来非常简单。两者都可以实现为执行就地分区,如下面的实现中的情况。除swap()之外,您在任何时候都不会看到存储在临时存储中的值。

    使用随机数据透视选择只是其中的一小部分。下面显示了如何初始化随机数生成器,并演示了您将要找到的最简单的分区算法和快速排序用法。

    除其他外,它演示了在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的模偏差,坦率地说,对于这个例子来说这样做太过分了。如果它很关键,我会完全使用另一台发电机。针对快速分区can be found at this post on this site选择随机数据透视位置的方法的优秀讨论,包括指向不同方法的许多链接。我建议复习一下。