在分区方法的两个while循环中,为什么看起来索引i是否超出数组的范围从一开始就没有被考虑?[这是Big Java的正确代码,I& #39;已经测试过,只是索引的东西让我感到困惑]
public void sort(int from, int to)
{
if (from >= to) return;
int p = partition(from, to);
sort(from, p);
sort(p + 1, to);
}
private int partition(int from, int to)
{
int pivot = a[from];
int i = from - 1;
int j = to + 1;
while (i < j)
{
i++; while (a[i] < pivot) i++;//here
j--; while (a[j] > pivot) j--;//here
if (i < j) swap(i, j);
}
return j;
}
答案 0 :(得分:1)
由于枢轴是从同一个数组中选择的,并且由于算法逻辑的实现方式,您永远不需要检查索引是否超出范围。在执行的某个时刻,条件必须变为真。
可以使用循环不变量证明算法的正确性。
1. private int partition(int from, int to)
2. {
3. int pivot = a[from];
4. int i = from - 1;
5. int j = to + 1;
6. while (i < j)
7. {
8. i++;
9. // at least one of a[i]...a[to] is greater than or equal to pivot
10. while (a[i] < pivot) i++;
11. j--;
12. // at least one of a[from]...a[j] is less than or equal to pivot
13. while (a[j] > pivot) j--;//here
14. if (i < j) swap(i, j);
15. // if i < j then at least one of a[i + 1]...a[to] is greater than or equal to pivot
16. // if i < j then at least one of a[from]...a[j - 1] is less than or equal to pivot
17. }
18. return j;
19. }
第9行和第12行(以及15,16)包含对于循环6到17的每次迭代都保持为true的不变量。从这些不变量中可以清楚地看出i
和j
索引永远不会走出数组边界。
我们只能证明第9行的不变量,第12行的不变量可以类似地证明。
对于第一次迭代,它是正确的,因为枢轴被选为a[from]
和i = from
。
在每次迭代结束时(包括第一次迭代),我们将位置i
的元素移动到大于或等于pivot的位置j
。因为i < j
然后第15行的不变量成立。在第8行递增i
后的下一次迭代中,不变量9变为有效,直接来自不变量15.通过归纳,我们可以得出结论,在循环6到17的每次迭代中,不变量9都是有效的。
如果我们选择pivot作为数组的最后一个元素,即[to]不变量仍然成立。但是我们需要在sort方法中更改流程。
sort(from, p == to ? p - 1 : p);
sort(p + 1, to);
而不是
sort(from, p);
sort(p + 1, to);
答案 1 :(得分:0)
在您的主partition
循环中,您可以看到i
和j
从数组的每一端开始,并朝着pivot
工作,而该位置的元素是枢轴<
或>
。它们都必须停在所选择的枢轴上,这样它们才能永远逃脱阵列。
int[] a;
private void sort() {
sort(0, a.length - 1);
}
public void sort(int from, int to) {
if (from >= to) {
return;
}
int p = partition(from, to);
sort(from, p);
sort(p + 1, to);
}
private int partition(int from, int to) {
int pivot = a[from];
int i = from - 1;
int j = to + 1;
while (i < j) {
i++;
while (a[i] < pivot) {
i++;
}
j--;
while (a[j] > pivot) {
j--;
}
if (i < j) {
swap(i, j);
}
}
return j;
}
private void swap(int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}
public void test() {
System.out.println("Hello");
a = new int[]{10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
sort();
System.out.println(Arrays.toString(a));
}
请注意,使用numbers[low]
作为枢轴只会降低性能 - 算法仍会正确排序数组。
答案 2 :(得分:0)
在第一次迭代中,两个索引都无法传递pivot
元素,因为i < pivotIndex < j
。因此,您无法在第一次迭代中传递边界(前提是索引在有效范围内且from <= to
;索引在循环之前的递增/递减语句之后也在范围内。)
在第一次索引之后的所有迭代中,索引不能小于from
或大于to
,因为i < j
和swap
- 在最后一次循环迭代中调用放置了分别在索引false
和i
处生成相应循环条件j
的元素:位置j
a[j] > pivot
处的元素为false,但该元素已移动定位i < j
和位置i
a[i] < pivot
的元素为false,但该元素已移至j > i
位置。