考虑到第一个元素作为支点,为什么我的指数不会超过数组的范围?

时间:2015-05-26 13:54:52

标签: java algorithm quicksort

分区方法的两个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;
   }

3 个答案:

答案 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的不变量。从这些不变量中可以清楚地看出ij索引永远不会走出数组边界。

我们只能证明第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循环中,您可以看到ij从数组的每一端开始,并朝着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 < jswap - 在最后一次循环迭代中调用放置了分别在索引falsei处生成相应循环条件j的元素:位置j a[j] > pivot处的元素为false,但该元素已移动定位i < j和位置i a[i] < pivot的元素为false,但该元素已移至j > i位置。