我是一名计算机科学专业的学生(刚刚开始),我正在从伪代码编写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++;
}
}
}
答案 0 :(得分:4)
这是来自Introduction to Algorithms的partition()
的伪代码,称为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)
有多种方法可以对快速排序进行分区,以下内容可能是最简单的方法。通常使用两个分区学校:
我更喜欢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选择随机数据透视位置的方法的优秀讨论,包括指向不同方法的许多链接。我建议复习一下。