我正在尝试实现此版本的合并排序,如Cormen的算法简介中所示。
public static void merge(int[] A, int p, int q, int r) {
int lengthOfLeftSubarray = q - p + 1;
int lengthOfRightSubarray = r - q;
int[] L = new int[lengthOfLeftSubarray + 1];
int[] R = new int[lengthOfRightSubarray + 1];
for(int i=0; i<lengthOfLeftSubarray; i++)
L[i] = A[p + i];
for(int i=0; i<lengthOfRightSubarray; i++)
R[i] = A[q + i];
L[lengthOfLeftSubarray] = -1;
R[lengthOfRightSubarray] = -1;
int i = 0, j = 0;
for(int k=p; k<r; k++) {
if(L[i] <= R[j]) {****
A[k] = L[i];
i++;
}
else {
A[k] = R[j];
j++;
}
}
}
public static void mergesort(int[] A, int p, int r) {
if(p < r){
int q = (p + r) / 2;
mergesort(A, p, q);
mergesort(A, q + 1, r);
merge(A, p, q, r);
}
}
public static void main(String[] args) {
int[] unsorted = {12, 16, 4, 2, 7, 6};
Sorting.mergesort(unsorted, 0, unsorted.length - 1);
System.out.println(Arrays.toString(unsorted));
}
我有两个问题:
merge
方法中抛出一个ArrayOutOfBounds异常,其中四颗星是( * *)。关于为什么会发生这种情况的任何想法?答案 0 :(得分:0)
粗略地看一眼(即我没有修复代码并在本地运行以确认测试用例有效),看起来有两个错误:
1)
for(int i=0; i<lengthOfRightSubarray; i++)
R[i] = A[q + i];
正确的子阵列应该从q + 1
开始,就像你进行递归调用一样:
mergesort(A, q + 1, r);
2)在OP评论中看起来你已经对它进行了排序,但你选择的标记值不起作用。 sentinel值的点取决于算法。在这种情况下,sentinel值的点是它应该大于输入数组unsorted
中的任何元素。特别是,当您执行merge
时,整个点就是您有两个已排序的子数组,并希望将它们合并为一个更大的且仍然排序的数组。如此快速的例子:
---- At the beginning ----
LEFT: {4, 12 16, INFINITY}
^
RIGHT: {2, 6, 7, INFINITY}
^
MERGED ARRAY: { }
^
---- After one iteration of the for loop ----
LEFT: {4, 12 16, INFINITY}
^
RIGHT: {2, 6, 7, INFINITY}
^
MERGED ARRAY: {2}
^
---- After second iteration of the for loop ----
LEFT: {4, 12 16, INFINITY}
^
RIGHT: {2, 6, 7, INFINITY}
^
MERGED ARRAY: {2, 4}
^
---- After third iteration of the for loop ----
LEFT: {4, 12 16, INFINITY}
^
RIGHT: {2, 6, 7, INFINITY}
^
MERGED ARRAY: {2, 4, 6}
^
---- After fourth iteration of the for loop ----
LEFT: {4, 12 16, INFINITY}
^
RIGHT: {2, 6, 7, INFINITY}
^
MERGED ARRAY: {2, 4, 6, 7}
^
你可以在这里看到为什么哨兵价值很重要。由于右侧索引指向INFINITY
,因此左侧子阵列中的元素将被适当地合并。将-1
替换为INFINITY
并轻松查看merge
触发ArrayOutOfBounds
例外很容易。
sentinel值有用的全部原因仅仅是因为当一个子数组耗尽值时我们不想处理这种情况。您可以轻松地实现该算法并检查子阵列何时耗尽元素(此时您只需将其与其他子阵列的其余部分填充),但它的清洁性稍差且难以推理。设置一个sentinel值可以确保子数组都不会耗尽允许您以更简单的方式编写证明(算法工作!)的元素。