簡體   English   中英

C隨機樞軸快速排序(改進分區功能)

[英]C randomized pivot quicksort (improving the partition function)

我是一名計算機科學專業的學生(剛剛開始),我正在從偽代碼編寫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++;
        }
    }
}

這是來自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. Squeeze - 折疊序列的兩端,直到找到合適的交換 ,然后將兩個元素交換到分區的正確邊。 實現起來並不簡單,但可以比替代方案更有效(減少交換計數)......
  2. 掃描 - 使用單個從左到右(或從右到左)掃描值,將值交換為遞增的樞軸索引,該索引在算法運行時移動。 實現起來非常簡單,如下所示。

我更喜歡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模偏差,坦率地說,對於這個例子來說這樣做會有點過頭了。 如果它很關鍵,我會完全使用另一台發電機。 有關為快速分配選擇隨機樞軸位置的方法的傑出討論可以在本網站的這篇文章中找到 ,包括許多指向不同方法的鏈接。 我建議復習一下。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM