简体   繁体   English

合并排序的Java实现不起作用

[英]Java implementation of Merge sort not working

I am trying to implement this version of merge sort, as seen in Introduction to Algorithms by Cormen. 我正在尝试实现此版本的合并排序,如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));
}

There are two issues that I have: 我有两个问题:

  1. In the book, a sentinel card is mentioned, which is supposed to be some sort of special value to put in an array, which won't interfere with the sorting. 在书中,提到了一个哨兵卡,这应该是放置在数组中的某种特殊值,不会干扰排序。 I have used -1 because I couldn't think of a way to use infinite , as suggested in the book. 我使用-1是因为我想不出书中建议的使用infinite的方法。 Could anyone explain what a sentinel is? 谁能解释一下哨兵是什么?
  2. The code is throwing an ArrayOutOfBounds exception in the merge method, where the four stars are ( * *). 代码在merge方法中引发ArrayOutOfBounds异常,其中四颗星为( * *)。 Any ideas as to why this is happening? 有什么想法为什么会这样?

On a cursory glance (ie I did not fix the code and run it locally to confirm the test case works), it looks like there's two bugs: 粗略浏览一下(即我没有修复代码并在本地运行以确认测试用例是否有效),看起来好像有两个错误:

1) 1)

for(int i=0; i<lengthOfRightSubarray; i++) 
    R[i] = A[q + i];

The right subarray should start at q + 1 just like it did when you did the recursive call: 正确的子数组应该从q + 1开始,就像您进行递归调用时一样:

mergesort(A, q + 1, r);

2) In the OP comments it looks like you sorted this out, but your choice of sentinel value doesn't work. 2)在OP注释中,看起来好像您已对它进行了整理,但是您选择的哨兵值无效。 The point of the sentinel value depends on the algorithm. 前哨值的点取决于算法。 In this case, the point of the sentinel value was that it was supposed to be larger than any element in the input array unsorted . 在这种情况下,前哨值的意义在于它应该大于输入数组unsorted中的任何元素。 In particular, when you do the merge the entire point is that you have two sorted sub arrays and want to merge them into one bigger, and still sorted , array. 特别是,当您进行merge时,最重要的一点是您拥有两个已排序的子数组,并希望将它们合并为一个更大且仍已排序的数组。 So quick example: 这么简单的例子:

---- 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}
                        ^

And you can see why the sentinel value is important here. 您会在这里看到为什么哨兵价值很重要。 Since the right index is pointing at INFINITY , the elements in the left sub array will be merged appropriately. 由于右索引指向INFINITY ,因此左子数组中的元素将被适当地合并。 It is easy to put -1 in place of INFINITY and watch your merge trigger an ArrayOutOfBounds exception. 很容易将-1替换为INFINITY并看着您的merge触发ArrayOutOfBounds异常。

The entire reason the sentinel value is useful just because we don't want to handle the case when one sub array runs out of values. 哨兵值之所以有用的整个原因仅仅是因为我们不想处理一个子数组的值用完的情况。 You could easily implement the algorithm and check when aa sub array runs out of elements (at which point you'd just fill it in with the rest of the other sub array), but it's slightly less clean and harder to reason about. 您可以轻松实现该算法并检查一个子数组何时元素用完(此时,您只需将其填充到其他子数组的其余部分中即可),但它的简洁性较差,难以推理。 Putting a sentinel value ensures that neither sub array will run out of elements which allows you to write the proof (the the algorithm works!) in a simpler way. 放置哨兵值可确保两个子数组都不会用完元素,这使您可以以更简单的方式编写证明(算法起作用!)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM