通过 1
在您的(原始)分区代码中,您有:
int partition(int a[], int start, int end){
int i,j,pivot,temp;
pivot = a[end]; //select last elem as the pivot
i = start- 1; // point i and j to before/after start end of the arr
j= end +1;
while(true){
//reduce j until we reach an elem that is less than pivot
if(a[j] >= pivot){ j= j-1; }
//increase i until we reach an elem that is more than pivot
if(a[i] <= pivot){ i = i+1; }
您应该只访问a[start] 到a[end],但您从访问这些边界之外的循环的第一次迭代开始。这肯定是问题的一部分。
修复不一定那么明显。放弃减法和加法是很诱人的,但这并不一定是正确的。关键的事情之一是获得足够有用的诊断打印以了解正在发生的事情。
通过 2
自第 1 阶段以来,已添加并修复了伪代码。伪代码与 Wikipedia 上的 QuickSort 非常相似。这是“Hoare Partitioning”主题的一个小变体——变体是在问题中使用repeat … until,而在维基百科中使用do … while。
algorithm quicksort(A, lo, hi) is
if lo < hi then
p := partition(A, lo, hi)
quicksort(A, lo, p)
quicksort(A, p + 1, hi)
algorithm partition(A, lo, hi) is
pivot := A[lo]
i := lo - 1
j := hi + 1
loop forever
do
i := i + 1
while A[i] < pivot
do
j := j - 1
while A[j] > pivot
if i >= j then
return j
swap A[i] with A[j]
你当前版本的分区码是:
int partition(int a[], int start, int end){
int i,j,pivot,temp;
pivot = a[end]; //select last elem as the pivot
i = start; // point i and j to before/after start end of the arr
j= end-1;
while(true){
//reduce j until we reach an elem that is less than pivot
if(a[j] >= pivot){ j= j-1; }
//increase i until we reach an elem that is more than pivot
if(a[i] <= pivot){ i = i+1; }
if(i<j){ //swap elems so that it is partitioned
temp = a[j];
a[j] = a[i];
a[i] = temp;
}
else{ return j; } //return the boundary for this partition
}
}
有许多关键区别:
- 您正在使用最后一个元素而不是第一个元素作为枢轴。
- 在递增
i 和递减 j 时,您根本没有循环。
解决这些问题会产生如下代码:
static inline void swap_ints(int *A, int i, int j)
{
int t = A[i];
A[i] = A[j];
A[j] = t;
}
static int partition(int a[], int start, int end)
{
int pivot = a[start]; // select first elem as the pivot
//printf("-->> P(%d,%d) - pivot %d\n", start, end, pivot);
int i = start - 1; // point i before start of the array
int j = end + 1; // point j after end of the array
while (true)
{
do
{
j--;
//printf("---- j a[%d] = %d\n", j, a[j]);
} while (a[j] > pivot);
do
{
i++;
//printf("---- i a[%d] = %d\n", i, a[i]);
} while (a[i] < pivot);
if (i >= j)
break;
//printf("-<>- a[%d]=%d, a[%d]=%d\n", j, a[j], i, a[i]);
swap_ints(a, i, j);
//printf("-><- a[%d]=%d, a[%d]=%d\n", j, a[j], i, a[i]);
//dump_data("Post-swap", a, start, end);
}
//dump_data("Partition", a, start, end);
//printf("<<-- P(%d) = %d\n", j, a[j]);
return j;
}
有很多被注释掉的打印功能,有时用来让我确信事情正在按预期工作。
您的主要快速排序功能还可以,尽管它也被(大量)印刷装饰:
void quicksort(int arr[], int start, int end)
{
if (start < end)
{
dump_data("QS Pre-partition", arr, start, end);
int boundary = partition(arr, start, end);
printf("QS Partition: %d:%d:%d\n", start, boundary, end);
dump_data("QS Pre-recursion L", arr, start, boundary);
quicksort(arr, start, boundary);
dump_data("QS Pre-recursion H", arr, boundary + 1, end);
quicksort(arr, boundary + 1, end);
dump_data("QS Post-Sort", arr, start, end);
}
}
dump_data() 函数如下所示:
void dump_data(const char *tag, int *data, int start, int end)
{
printf("%s (%d):", tag, end - start + 1);
for (int i = start; i <= end; i++)
{
printf(" %d", data[i]);
}
putchar('\n');
}
测试代码(main())看起来像:
int main(void)
{
int data[] =
{
/* random -n 20 0 9 | commalist -b ' ' -n 10 */
//4, 8, 0, 0, 3, 8, 3, 6, 5, 9,
//6, 8, 3, 6, 5, 5, 0, 8, 1, 1,
//3, 9, 4, 7, 2, 6, 9, 0, 6, 1,
//8, 0, 2, 1, 4, 0, 6, 5, 4, 2,
//7, 6, 2, 5, 4, 4, 6, 0, 8, 3,
//6, 1, 2, 7, 4, 3, 0, 0, 0, 4,
//4, 7, 8, 8, 4, 4, 4, 4, 9, 6,
//9, 0, 2, 7, 6, 5, 9, 2, 7, 7,
9, 7, 0, 9, 5, 4, 8, 7, 9, 9,
2, 9, 9, 7, 0, 3, 9, 6, 8, 5,
//5, 1, 4, 5, 5, 4, 0, 2, 6, 1,
//5, 8, 1, 0, 1, 9, 8, 4, 8, 0,
};
enum { NUM_DATA = sizeof(data) / sizeof(data[0]) };
int data_copy[NUM_DATA];
for (int end = 1; end < NUM_DATA; end++)
{
memcpy(data_copy, data, NUM_DATA * sizeof(data[0]));
dump_data("Unsorted", data_copy, 0, end);
quicksort(data_copy, 0, end);
dump_data("PostSort", data_copy, 0, end);
check_sorted(data_copy, 0, end);
}
return 0;
}
check_sorted() 函数通常什么都不说(但它在用于未排序的数据时确实如此):
void check_sorted(int *data, int lo, int hi)
{
for (int i = lo + 1; i < hi; i++)
{
if (data[i-1] > data[i])
printf("Inversion @ A[%d] = %d vs A[%d] = %d\n", i-1, data[i-1], i, data[i]);
}
}
main() 程序中有 0..9 范围内的各种 20 个随机数集。
仔细重读第N次次维基百科页面显示:
分区: 对数组重新排序,使所有值小于枢轴的元素都在枢轴之前,而所有值大于枢轴的元素都在它之后(相等的值可以去任何一种方式) .
此尾端评论的先前版本担心有时会出现在分区数据的下半部分中的枢轴值,但指出排序仍然有效。引用表明这是完全允许的——没有问题。有一些分区方案,例如 fat pivot 方案,这是不正确的。