英文:
Quicksort implementation correctness
问题
这个实现可能在某些情况下失败吗?我理解它在某些情况下可能运行得很慢,但我现在更担心的是正确性。
class QuickSort {
public int findpivot(int[] nums, int start, int end) {
int pivot = nums[start];
int left = start + 1;
int right = end;
while (left <= right) {
while (left <= right && nums[left] <= pivot)
left++;
while (left <= right && nums[right] > pivot)
right--;
if (left < right) {
var tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
left++;
right--;
}
}
int tmp = nums[right];
nums[right] = nums[start];
nums[start] = tmp;
return right;
}
public void sort(int[] nums, int start, int end) {
if (start < end) {
int pivot = findpivot(nums, start, end);
sort(nums, start, pivot - 1);
sort(nums, pivot + 1, end);
}
}
public int[] sortArray(int[] nums) {
sort(nums, 0, nums.length - 1);
return nums;
}
}
英文:
Is there any case where this implementation might fail? I understand that it might run very slowly for some cases, but the my worry for now is more about correctness
class QuickSort {
public int findpivot(int []nums, int start, int end) {
int pivot = nums[start];
int left = start+1;
int right = end;
while(left<=right) {
while(left<=right && nums[left]<=pivot)
left++;
while(left<=right && nums[right]>pivot)
right--;
if(left<right) {
var tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
left++;
right--;
}
}
int tmp = nums[right];
nums[right] = nums[start];
nums[start] = tmp;
return right;
}
public void sort(int []nums, int start, int end) {
if(start<end) {
int pivot = findpivot(nums, start, end);
sort(nums,start,pivot-1);
sort(nums,pivot+1,end);
}
}
public int[] sortArray(int[] nums) {
sort(nums,0,nums.length-1);
return nums;
}
答案1
得分: 2
这个实现能够正确排序数组。
它综合了霍尔(Hoare)和洛穆托(Lomuto)算法中的思想:
- 中心值的位置被排除在迭代的范围之外,在循环完成后,它会被交换到正确的位置,即分区之间的位置,这样函数就能返回分隔这两个分区并且包含中心值的索引。这意味着调用者可以在递归调用时排除这个索引。这是洛穆托算法使用的原则。
- 循环使用两个指标,这两个指标从循环范围的两端逐渐接近,确保超出这个缩小的窗口的值位于正确的分区中,这实现了霍尔算法。
可能会对最后一次交换产生担忧,因为left
和right
可能相等,也可能互相超越。首先,我们可以确定外循环至少执行一次,因为只有在start < end
时才调用findPivot
函数。当循环结束时,我们可以确信right
是一个有效的索引,即我们有start <= right <= end
。我们也可以确定nums[right] <= pivot
,因为要么right == start
(且nums[start] == pivot
),要么right
是已经用left
处理过的索引。因此,最终的交换将把该值移到正确的分区中。
基于这些观察,我们可以说该算法将正确地对数组进行排序。
确保代码正确的一项工作是使用不同大小的随机输入对其进行压力测试。
以下是执行该操作的一些代码:
for (int size = 0; size < 50; size++) {
System.out.println("Testing with arrays of size " + size);
int[] nums = new int[size];
// 用排序好的值创建数组列表
List<Integer> lst = new ArrayList<>();
for (int i = 0; i < size; i++) {
lst.add(i);
}
for (int c = 0; c < 100; c++) {
// 洗牌
Collections.shuffle(lst);
for (int i = 0; i < size; i++) {
nums[i] = lst.get(i);
}
qs.sortArray(nums);
// 验证
for (int i = 0; i < size; i++) {
if (nums[i] != i) System.out.println("ERROR");
}
}
}
System.out.println("Tests completed");
英文:
This implementation correctly sorts arrays.
It is a mix of ideas from Hoare's and Lomuto's algorithms:
- The pivot value's location is excluded from the range that is iterated, and after the loop completes, it is swapped into position -- between the partitions -- so that the function can return the index that separates the two partitions and which has the pivot value. That means the caller can exclude this index from recursive calls. This is a principle that is used by Lomuto's algorithm.
- The loop uses two indices that approach each other from both ends of the loop's range, ensuring that the values that are outside of this shrinking window are located in the correct partition: this implements Hoare's algorithm.
Some worry could arise about the last swap, because left
and right
could either be equal or could have overtaken each other. First of all, we can be sure the outer loop executes at least once, because the findPivot
function is only called when start<end
. When the loop ends, we can be sure that right
is a valid index, i.e. we have start<=right<=end
. We can also be sure nums[right]<=pivot
because either right==start
(and nums[start]==pivot
) or right
is an index that was already processed with left
. Therefore the final swap will move that value to the correct partition.
With these observations we can say the algorithm will correctly sort an array.
One of the things to do to ensure the code is correct, is to stress test it with random input of differing sizes.
Here is some code that does that:
for (int size = 0; size < 50; size++) {
System.out.println("Testing with arrays of size " + size);
int[] nums = new int[size];
// Make array list with sorted values
List<Integer> lst = new ArrayList<>();
for (int i = 0; i < size; i++) {
lst.add(i);
}
for (int c = 0; c < 100; c++) {
// Shuffle
Collections.shuffle(lst);
for (int i = 0; i < size; i++) {
nums[i] = lst.get(i);
}
qs.sortArray(nums);
// Verify
for (int i = 0; i < size; i++) {
if (nums[i] != i) System.out.println("ERROR");
}
}
}
System.out.println("Tests completed");
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论