修改此快速排序以始终使用最后一个元素作为基准
我有以下 Quicksort ,它始终选择子序列的第一个元素作为其枢轴:
void qqsort(int array[], int start, int end) {
int i = start; // index of left-to-right scan
int k = end; // index of right-to-left scan
if (end - start >= 1) { // check that there are at least two elements to sort
int pivot = array[start]; // set the pivot as the first element in the partition
while (k > i) { // while the scan indices from left and right have not met,
while (array[i] <= pivot && i <= end && k > i) // from the left, look for the first element greater than the pivot
i++;
while (array[k] > pivot && k >= start && k >= i) // from the right, look for the first element not greater than the pivot
k--;
if (k > i) // if the left seekindex is still smaller than the right index, swap the corresponding elements
swap(array, i, k);
}
swap(array, start, k); // after the indices have crossed, swap the last element in the left partition with the pivot
qqsort(array, start, k - 1); // quicksort the left partition
qqsort(array, k + 1, end); // quicksort the right partition
} else { // if there is only one element in the partition, do not do any sorting
return;
}
}
现在,如您所见,该算法始终将第一个元素作为主元: int hub = array[start];
我想修改此算法,使其始终使用最后一个元素而不是子序列的第一个元素,因为我想分析两种实现的物理运行时间。
我尝试将行 intivot = array[start];
更改为 intivot=array[end];
但算法随后输出了未排序的序列:
//Changes: int pivot = array[end];
unsorted: {5 4 3 2 1}
*sorted*: {1 2 5 4 3}
要测试另一个数据透视表,我也尝试使用子序列的中心元素,但算法仍然失败:
//Changes: int pivot = array[(start + end) / 2];
unsorted: {5 3 4 2 1}
*sorted*: {3 2 4 1 5}
有人可以帮助我正确理解这个算法,并告诉我需要做哪些更改才能成功地让这个实现始终选择子序列的最后一个元素作为枢?
I have the following Quicksort that always chooses the first element of the subsequence as its pivot:
void qqsort(int array[], int start, int end) {
int i = start; // index of left-to-right scan
int k = end; // index of right-to-left scan
if (end - start >= 1) { // check that there are at least two elements to sort
int pivot = array[start]; // set the pivot as the first element in the partition
while (k > i) { // while the scan indices from left and right have not met,
while (array[i] <= pivot && i <= end && k > i) // from the left, look for the first element greater than the pivot
i++;
while (array[k] > pivot && k >= start && k >= i) // from the right, look for the first element not greater than the pivot
k--;
if (k > i) // if the left seekindex is still smaller than the right index, swap the corresponding elements
swap(array, i, k);
}
swap(array, start, k); // after the indices have crossed, swap the last element in the left partition with the pivot
qqsort(array, start, k - 1); // quicksort the left partition
qqsort(array, k + 1, end); // quicksort the right partition
} else { // if there is only one element in the partition, do not do any sorting
return;
}
}
Now as you can see, this algorithm always takes the first element to be the pivot: int pivot = array[start];
I want to modify this algorithm to make it always use the last element instead of the first element of the subsequence, because I want to analyze the physical running times of both implementations.
I tried changing the line int pivot = array[start];
to int pivot = array[end];
but the algorithm then outputted an unsorted sequence:
//Changes: int pivot = array[end];
unsorted: {5 4 3 2 1}
*sorted*: {1 2 5 4 3}
To test another pivot, I also tried using the center element of the subsequence but the algorithm still failed:
//Changes: int pivot = array[(start + end) / 2];
unsorted: {5 3 4 2 1}
*sorted*: {3 2 4 1 5}
Can someone please help me understand this algorithm correctly and tell me what changes do I need to make to successfully have this implementation always choose the last element of the subsequence as the pivot?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
问题的原因
问题是您使用了
int k = end;
。当您将枢轴元素作为数组中的第一个元素时,使用 int i = start; 就可以了,因为循环中的检查将略过它(array[i]array[i]
array[i]
= 枢轴
)。但是,当您使用最后一个元素作为枢轴时,k 会在结束索引处停止并将枢轴切换到分区左半部分的位置。您已经遇到麻烦了,因为您的枢轴很可能位于左侧分区内部的某个位置而不是边界。解决方案
要解决此问题,当您使用最右边的元素作为基准时,需要设置
int k = end - 1;
。您还需要更改用于将枢轴交换到左侧和右侧分区之间的边界的行:为此,您必须使用 i ,因为 i 将最终位于右侧分区的最左侧元素(然后可以将其与枢轴位于最右边的元素中,它将保留顺序)。最后,您需要将
k >= i
更改为k > i
在同时递减 k 的同时,否则数组 [-1] 索引错误会发生微小变化。这在以前是不可能发生的,因为此时 i 始终至少等于 i+1。应该可以做到这一点。
旁注:
这是一个写得不好的快速排序,我不建议学习。它有一些无关的、不必要的比较以及其他一些我不会浪费时间列出的错误。我建议使用 Sedgewick 和 Bentley 的本演示文稿中的快速排序。
The Cause of the Problem
The problem is that you use
int k = end;
. It was fine to useint i = start;
when you had the pivot element as the first element in the array because your checks in the loop will skim past it (array[i] <= pivot
). However, when you use the last element as the pivot, k stops on the end index and switches the pivot to a position in the left half of the partition. Already you're in trouble because your pivot will most likely be somewhere inside of the left partition rather than at the border .The Solution
To fix this, you need to set
int k = end - 1;
when you use the rightmost element as the pivot. You'll also need to change the lines for swapping the pivot to the border between the left and right partitions:You have to use i for this because i will end up at the leftmost element of the right partition (which can then be swapped with the pivot being in the rightmost element and it will preserver the order). Lastly, you'll want to change
k >= i
tok > i
in the while which decrements k or else there is small change of an array[-1] indexing error. This wasn't possible to happen before because i always at least was equal to i+1 by this point.That should do it.
Sidenote:
This is a poorly written quicksort which I wouldn't recommend learning from. It has a some extraneous, unnecessary comparisons along with some other faults that I won't waste time listing. I would recommend using the quicksorts in this presentation by Sedgewick and Bentley.
我没有测试它,但无论如何检查一下:
这
可能应该是
如果我们选择
end
作为枢纽,或类似的东西。 编辑:这是一种有趣的分区算法,但它不是标准算法。
嗯,枢轴在分区逻辑中是固定的。
该算法将第一个元素视为 Head,其余元素视为要分区的 Body。
分区完成后,作为最后一步,头部(枢轴)与左侧分区部分的最后一个元素交换,以保持顺序。
我想在不改变算法的情况下使用不同的枢轴的唯一方法是:
I didn't test it, but check it anyway:
this
probably should be
or something similar, if we choose
end
as pivot.Edit: That's an interesting partitioning algorithm, but it's not the standard one.
Well, the pivot is fixed in the logic of the partitioning.
The algorithm treats the first element as the Head and the rest elements as the Body to be partitioned.
After the partitioning is done, as a final step, the head (pivot) is swapped with the last element of the left partitioned part, to keep the ordering.
The only way I figured to use a different pivot, without changing the algorithm, is this:
第一个提示:如果数据是随机的,那么平均而言,选择哪个值作为主元并不重要。真正提高枢轴“质量”的唯一方法是采用更多(例如 3)个指数并使用其中值的中值。
第二个提示:如果更改主元值,还需要更改主元索引。这没有明确命名,但 array[start] 在某一点被交换到已排序子序列的“中间”。您需要相应地修改这一行。如果您采用的索引不在子序列的边缘,则需要在迭代之前先将其交换到边缘。
第三个提示:您提供的代码注释过多。您应该能够真正理解这个实现。
First hint: If the data are random, it does not matter, on the average, which value you choose as pivot. The only way to actually improve the "quality" of the pivot is to take more (e.g. 3) indices and use the one with median value of these.
Second hint: If you change the pivot value, you also need to change the pivot index. This is not named explicitly, but
array[start]
is swapped into the "middle" of the sorted subsequence at one point. You need to modify this line accordingly. If you take an index which is not at the edge of the subsequence, you need to swap it to the edge first, before the iteration.Third hint: The code you provided is excessively commented. You should be able to actually understand this implementation.
放置一个
在初始化枢轴之前
Put a single
before initializing pivot
如果您开始监视从数组的第一个元素到最后一个 - 1 的每个元素,并在每次递归时将最后一个元素保留为枢轴,那么您将在精确的 O(nlogn) 时间内得到答案。
If you start monitoring each element from the 1st element of the array to the last - 1, keeping the last element as the pivot at every recursion, then you will get the answer in exact O(nlogn) time.