【问题标题】:mergesorting 3 sub-arrays at once一次合并3个子数组
【发布时间】:2012-05-27 07:54:15
【问题描述】:

我正在制作一个实现归并排序算法的程序,但不是每次将它们分成两部分,而是每次将它们分成三部分并递归地对它们进行归并排序。万一我把你弄糊涂了,它基本上是一个归并排序,但不是用 2 个部分归并排序,而是每次归并排序 3,听起来很有趣吧?好吧,绝对不是。

这是我的合并排序实现:

public static void mergesort(int[] data) {
    int elements = data.length;
    int sizeLeft;
    int sizeCenter;
    int sizeRight;

    if (elements > 2) {

        if (elements % 3 == 0) {
            sizeLeft = elements / 3;
            sizeCenter = elements / 3;
            sizeRight = elements / 3;
        } else if (elements % 3 == 1) {
            sizeLeft = (elements / 3) + 1;
            sizeCenter = elements / 3;
            sizeRight = elements / 3;
        } else { //if (elements % 3 == 2)
            sizeLeft = (elements / 3) + 1;
            sizeCenter = elements / 3;
            sizeRight = (elements / 3) + 1;
        }

        int[] left = makeArray(data, 0, sizeLeft);
        int[] center = makeArray(data, sizeLeft, sizeCenter);
        int[] right = makeArray(data, sizeLeft + sizeCenter, sizeRight);

        mergesort(left);
        mergesort(center);
        mergesort(right);

        merge(data, left, center, right);
    }
}

这里是合并方法:

public static void merge(int[] data, int[] left, int[] center, int[] right) {
    int[] temp = new int[left.length + center.length + right.length];
    int copiedTotal = 0;
    int copiedLeft = 0;
    int copiedCenter = 0;
    int copiedRight = 0;

    while ((copiedLeft < left.length)
            && (copiedCenter < center.length)
            && (copiedRight < right.length)) {

        if ((left[copiedLeft] < center[copiedCenter])
                && (left[copiedLeft] < right[copiedRight])) {

            temp[copiedTotal++] = left[(copiedLeft++)];
        } else if ((center[copiedCenter] < left[copiedLeft])
                && (center[copiedCenter] < right[copiedRight])) {
            temp[copiedTotal++] = center[copiedCenter++];
        } else {
            temp[copiedTotal++] = right[copiedRight++];
        }
    }

    while ((copiedLeft < left.length) && (copiedCenter < center.length)) {
        if (left[copiedLeft] < center[copiedCenter]) {
            temp[copiedTotal++] = left[copiedLeft++];
        } else{
            temp[copiedTotal++] = center[copiedCenter++];
        }
    }

    while ((copiedLeft < left.length) && (copiedRight < right.length)) {
        if (left[copiedLeft] < right[copiedRight]) {
            temp[copiedTotal++] = left[copiedLeft++];
        } else{
            temp[copiedTotal++] = right[copiedRight++];
        }
    }

    while ((copiedCenter < center.length) && (copiedRight < right.length)) {
        if (center[copiedCenter] < right[copiedRight]) {
            temp[copiedTotal++] = center[copiedCenter++];
        } else{
            temp[copiedTotal++] = right[copiedRight++];
        }
    }

    while (copiedLeft < left.length) {
        temp[copiedTotal++] = left[copiedLeft++];
    }

    while (copiedCenter < center.length) {
        temp[copiedTotal++] = center[copiedCenter++];
    }

    while (copiedRight < right.length) {
        temp[copiedTotal++] = right[copiedRight++];
    }
    System.arraycopy(temp, 0, data, 0, left.length + center.length + right.length);
//        for (int i = 0; i < data.length; i++) {
//            if ((copiedRight >= right.length) && (copiedCenter >= center.length)) {
//                data[i] = left[copiedLeft];    // take from left
//                copiedLeft++;
//            } else if ((copiedRight >= right.length) && (copiedLeft >= left.length)) {
//                data[i] = center[copiedCenter];    // take from left
//                copiedCenter++;
//            } else if ((copiedCenter >= center.length) && (copiedLeft >= left.length)) {
//                data[i] = right[copiedRight];    // take from left
//                copiedRight++;
//            } else if ((copiedLeft < left.length
//                    && left[copiedLeft] <= right[copiedRight])
//                    && left[copiedLeft] <= center[copiedCenter]) {
//
//                data[i] = left[copiedLeft];    // take from left
//                copiedLeft++;
//
//            } else if ((copiedRight >= right.length) && (copiedLeft >= left.length)
//                    || (copiedCenter < center.length
//                    && center[copiedCenter] <= right[copiedRight])
//                    && center[copiedCenter] <= left[copiedLeft]) {
//
//                data[i] = center[copiedCenter];    // take from center
//                copiedCenter++;
//            } else {
//                data[i] = right[copiedRight];
//                copiedRight++;// take from center
//            }
//
//        }
    }
}

在合并方法内的 cmets 中,我尝试制作另一种合并方法,但结果并不好,事情变得更加复杂,但我把它留在那里以供参考。

问题是这根本不起作用,例如,如果我有:

输入:6 5 4 3 2 1

那我就有了:

输出:[2, 1, 4, 3, 6, 5]

老实说,我为此非常努力,连续 2 天,我发现只有两个人听说过这种合并排序,在谷歌搜索数小时后,我在这里只发现了一个类似的问题(这太复杂了,无法理解)以及 wiki 答案中的另一个线程从未得到回答。

任何帮助都将不胜感激,当然我并不是在寻求直接的解决方案,因为我正在努力学习,但是提示和提示以及我做错了什么会对我有很大帮助。

提前致谢。

【问题讨论】:

  • 你用调试器解决过这个问题吗?
  • 另外 Vipar 熟悉合并排序算法的人会看到,这不仅仅是一些大代码墙,而是用于 3 个子数组而不是 2 个子数组的更改,因为我花了这么多时间搜索和查看调试器并重新编写代码我显然不是在这里寻找简陋和现成的代码,我的问题很清楚,再次阅读我只是在寻求提示
  • 这是一个很好的例子,说明与算法的大 O 相关的常数如何使某些技术效率低下。

标签: java arrays merge divide-and-conquer


【解决方案1】:

问题似乎在于,当您拥有一个 2 元素数组时,您不会对其进行任何操作。你应该对其进行排序。如果你举个例子:[6,5,4,3,2,1],在递归的第二步你有[2,1]; [4,3] 和 [6,5] 并像这样合并它们。如果您先对它们进行排序,您将获得正确的顺序。为了在合并功能中对它们进行排序,您应该添加:

if ((elements==2)&&(data[1]<data[0])){
 int aux = data[1];
 data[1] = data[0];
 data[0] = aux;

}

希望对你有帮助。

更新

如果你想要一个纯粹的合并排序,你可以尝试(正如我在评论中解释的那样)添加以下代码:

if (elements==2){
 int[] center = [];
 int[] left = makeArray(data,0,1);
 int[] right =makeArray(data,1,1);

 mergesort(left); //you can call these methods or not, on a empty or 1 element array they dont have an effect
 mergesort(center);
 mergesort(right);

 merge(data, left, center, right); //it should work well when center is an empty array

}

更新 2 你可以重构我展示的代码,让它看起来很漂亮。基本思想是您可以在 Java 中拥有一个空数组,并且您的合并函数可以正确处理它。希望我的观点更清楚一点。

【讨论】:

  • +1 我想知道if (elements &gt; 2) {,但我没有把它放在一起。
  • 是的,我想到了,但问题在于它似乎不完全是一个合并排序,而是类似于 2 个部分的合并排序和 3 个部分的合并排序,我只是不确定是否这将有资格作为 3 部分合并排序
  • 如果你想有一个纯粹的合并排序,你可以尝试以下方法:当元素为 2 时,只需让 center = [], left=[data[0]] 和 right = [数据[1]]。我会用代码更新答案。
  • 关于您的更新,然后您暗示我应该有第二个合并排序,它是一个常规合并排序(拆分为 2)并使用该合并排序对两个左右数组进行排序?只是我在 java 中不能有一个空数组(中心数组),即使我不能在我创建的合并排序中产生问题?
  • 您可以在 Java 中拥有一个空数组,并且通过读取您的合并函数它应该可以正常工作(认为空数组的长度为 0)。我的意思是你可以用空数组调用你的合并排序函数。我将对答案进行第二次更新以更清楚。
猜你喜欢
  • 1970-01-01
  • 2013-08-28
  • 2012-01-27
  • 2012-06-30
  • 1970-01-01
  • 1970-01-01
  • 2017-12-23
  • 1970-01-01
  • 2021-06-05
相关资源
最近更新 更多