【问题标题】:What is wrong with my Merge Sort code?我的合并排序代码有什么问题?
【发布时间】:2016-03-18 00:23:07
【问题描述】:

我在计算机课上遇到了合并排序问题。我不断收到错误或返回的原始 ArrayList。

我相信合并排序涉及递归地将数组(列表)分成两半,直到只剩下一个元素,然后从这些单独的元素开始工作,按排序顺序合并它们。这一直持续到对数组(列表)进行排序。至于实际的排序部分,我试图在新的 ArrayList 中插入两半之间的较高值,直到它们都为空,在这种情况下,填充的 ArrayList 现在已排序。

这是我当前的代码:

public static ArrayList<Integer> mergesort(ArrayList<Integer> arr) {

    // Base case: if size of ArrayList is 1, return it.
    if (arr.size() < 2) {
        return arr;
    }

    // Else: Find the middle index.
    int middle = (arr.size() - 1) / 2;

    // Split into left and right halves.
    ArrayList<Integer> leftHalf = new ArrayList<Integer>();
    for (int i = 0; i < middle; i++)
        leftHalf.add(arr.get(i));

    ArrayList<Integer> rightHalf = new ArrayList<Integer>();
    for (int j = middle; j < arr.size(); j++)
        rightHalf.add(arr.get(j));

    // Recurse using the halves.
    mergesort(leftHalf);
    mergesort(rightHalf);

    // Sort and merge the two halves.
    return merge(leftHalf, rightHalf, arr);
}

// Merge two halves and sort them, and put the sorted values into the ArrayList sorted.
public static ArrayList<Integer> merge(ArrayList<Integer> arr1, ArrayList<Integer> arr2, ArrayList<Integer> sorted) {

    // While the ArrayLists are not empty, add sorted elements to ArrayList sorted.
    while (arr1.size() > 0 && arr2.size() > 0) {

        // If the first element in A is greater than the first in B, remove A and add to ArrayList sorted.
        if (arr1.get(0) >= arr2.get(0)) {
            sorted.add(arr1.get(0));
            arr1.remove(0);
        }
        // Else, remove from B and add to ArrayList sorted.
        else {
            sorted.add(arr2.get(0));
            arr2.remove(0);
        }
    }

    // If there're still elements in A due to arr being odd
    // add them to C since they will be the largest.
    if (arr1.size() > 0)
        sorted.add(arr1.get(0));

    return sorted;
}

如果有任何帮助,我将不胜感激,但请不要给我合并排序的完整实现,因为我想真正了解如何在未来执行此操作。

【问题讨论】:

  • 最简单的未正确排序的示例是什么?当您在调试器中单步执行此代码时会看到什么?
  • 顺便说一句,要提高性能,请尝试这样做,不要创建任何新的 ArrayLists,也不要复制它们,也不要使用昂贵的 remove(0)
  • 我总是返回原始数组,即使是简单的事情。我认为我对递归还没有扎实的掌握,所以我很难确定问题是什么。
  • 如果不创建新的 ArrayList,我将如何做到这一点?我想我必须提高内存效率,因为我得到了 java.lang.OutofMemoryError
  • 尝试创建一个方法mergesort(List&lt;Integer&gt; list, int start, int end),它不会从列表中删除任何内容,但可以对值进行适当的排序。这不是 CPU 效率最高的,但不会产生额外的内存,尽管您可以创建一个方法 mergesort(List&lt;integer&gt; list, int start, int end, List&lt;Integer&gt; scratchList),其中暂存列表用作可重用列表。这最多会使您的内存使用量增加一倍。顺便说一句,如果您可以使用 int[] 而不是 List&lt;Integer&gt; 这可以是内存使用量的 1/5。

标签: java arrays sorting arraylist merge


【解决方案1】:

我看到了两个直接的问题。

首先,你有无限递归。假设您传入一个 4 值列表并打印列表大小和中间值,您将得到:

size 4, middle 1 size 3, middle 1 size 2, middle 0 size 2, middle 0 size 2, middle 0 ... to infinity

请记住,Java 已经在您的代码中进行整数除法。

其次,您将arr 参数从mergesort 传递到merge,没有任何东西被从中删除。因此,即使您通过了无限递归,最终您仍然会得到一个更大的列表,您的原始列表加上您 .add()ed 给它的任何内容

【讨论】:

  • 感谢您的帮助!我将如何修复合并排序中的参数?
  • @JunlinYi 根据您已经编写的内容,您希望merge 函数中的sorted 是一个新的本地列表,然后返回。然后,您的 mergesort 函数会将 leftHalfrightHalf 设置为它们各自的 mergesort 调用的返回值。这种方法意味着不再需要 merge 中的 sorted 参数。这不是修复现有问题的唯一方法,但由于 Java 传递对象的方式,它可能是最容易理解的。即使你修复了它,你也会发现你的输出不是你想要的,但修复应该很容易。
【解决方案2】:

正如 MarquisDeMizzle 所说,当 arr.size() == 1 时,中间 == 1。

    int middle = arr.size() / 2;

在主合并循环复制所有剩余元素之后,由于您不知道 arr1 或 arr2 是否先为空,请同时检查:

    // copy any remaining elements
    while (arr1.size() > 0)
        sorted.add(arr1.get(0));

    while (arr2.size() > 0)
        sorted.add(arr2.get(0));

如果有兴趣,您可能想学习自下而上的归并排序。它跳过所有递归,并开始假设具有 n 个元素的数组是 n 次大小为 1 的运行,仅使用索引在原始数组和临时数组之间来回合并。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-14
    • 1970-01-01
    • 2017-05-30
    • 2021-11-14
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 2023-04-10
    相关资源
    最近更新 更多