[英]Quicksort implementation bug
一段时间后,我尝试自己实现 quickSort。 对我来说,实现看起来不错,但一些测试用例显然失败了。
class MyImplementation {
public int[] sortArray(int[] nums) {
quicksort(nums, 0, nums.length - 1);
return nums;
}
private void quicksort(int[] nums, int lo, int hi){
if(lo < hi){
int j = partition(nums, lo, hi);
quicksort(nums, lo, j-1);
quicksort(nums, j+1, hi);
}
}
private int partition(int[] nums, int lo, int hi){
int pivot = nums[lo];
int head = lo + 1;
int tail = hi;
while(head < tail){
while(head < hi && nums[head] < pivot) ++head;
while(nums[tail] > pivot) --tail;
if(head < tail){
swap(nums, head, tail);
++head;
--tail;
}
}
if(nums[head] < pivot)
swap(nums, lo, head);
else
swap(nums, lo, head - 1);
return head;
}
private void swap(int[] nums, int i, int j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
它在许多情况下都会过去,例如:-
[5,2,3,1]
[5,1,1,2,0,0]
[7,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5]
但它在以下测试用例中失败了:-
[5,71,1,91,10,2,0,0,13,45,7]
我得到的 output 是:-
[0,0,1,2,5,10,7,71,13,45,91]
代替:-
[0,0,1,2,5,7,10,13,45,71,91]
我知道,我可以从某个地方复制以获得正确工作的代码,但是我们可以从算法的角度找出为什么这段代码不起作用吗? 这段代码的逻辑缺陷是什么? 我们如何才能以最少的更改解决此问题并获得完美运行的代码?
这是相当微妙的。 错误在这里:
private void quicksort(int[] nums, int lo, int hi){
if(lo < hi){
int j = partition(nums, lo, hi);
quicksort(nums, lo, j-1);
quicksort(nums, j+1, hi);
// ^^^ j+1 must be j
}
}
最微妙的部分是,通常不需要使用j
作为“高”部分的起点进行递归,但您的分区算法需要它。 所以你会看到各种在不同分区上递归的例子,它们也可以是正确的,结合它们使用的任何分区算法,但是混合的自由度不高。
您的分区算法基于 Hoare 的分区算法,而不是 Lomuto 的算法。 Lomuto 的分区算法将 pivot 放在其最终的 position 中并返回其索引,以便在递归中跳过索引。 Hoare 的分区算法或您的变体并不能完全做到这一点。 例如,在采用分区算法末尾的else
分支的情况下,pivot 以head - 1
结束,而head
处的元素具有不小于 pivot 的任意值,我们所知道的是它属于在“高”分区中,但尚未排序,不能跳过。
结合 Hoare 分区,通常会看到 [lo..p](其中 p 是分区算法返回的索引)和 [p+1..hi] 上的递归,但这在这种情况下不合适因为它的版本返回head
而不是tail
,有效地使 p 高了一个,所以分区变成了 [lo.. p-1] 和 [p.. hi]。
当前接受的答案解决了这个问题,但实际上,Lomuto 算法应该能够使用j+1
作为第二个分区的开始,因为 pivot 预计在j
处,因此不需要成为递归排序的一部分.
真正的问题是您的partition
方法并不总是返回 pivot 的索引:
if(nums[head] < pivot)
swap(nums, lo, head);
else
swap(nums, lo, head - 1);
return head;
在else
的情况下, pivot 不会交换到索引head
,而是交换到head - 1
,因此在这种情况下返回的索引应该是head - 1
。
这是一个简单的修复:
if(nums[head] < pivot)
swap(nums, lo, head);
else
swap(nums, lo, --head);
return head;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.