【问题标题】:Merge Sort Java Implementation so merge works within one array with second splitted array reversed合并排序 Java 实现,因此合并在一个数组内工作,第二个拆分数组反转
【发布时间】:2021-04-05 12:28:29
【问题描述】:

挑战:在关于数据结构和算法的讲座中,我遇到了一个合并排序版本,它使用合并例程,其后半部分与拆分索引相反,并从那里比较第一个和最后一个元素。我尝试用java实现,但总是以某种方式失败。

问题:正在对数组进行排序,因此输出为[1, 2, 4, 8, 6],因此6 未排序。似乎递归调用没有在最后一次 merge 调用中查看元素 6

我尝试了什么:移动不同的索引并添加不同的打印语句进行检查。 我试图在merge 内的最后一个for 循环之前制作j = r,这每次都会导致堆栈溢出。我试图改变计算数组大小的方式,因为我不确定伪代码是否除了数组从 1 或 0 开始。我试图将 if(p < r-1) 转换为 if(p <= r-1) 但得到一个 堆栈溢出。

我查看了 java 合并例程的不同实现,到目前为止,我发现的每一个似乎都适用于两个数组。是否有严重的原因导致上述方法无法正常工作或知道如何解决此问题?

给定以下伪代码:

void merge_sort(array<T>& A, int p, int r) {
    if (p < r - 1) {
        int q = Floor((p + r) / 2);
        merge_sort(A, p, q);
        merge_sort(A, q + 1, r);
        merge(A, p, q, r);
    }
}

void merge(array<T>& A, int p, int q, int r) {
    array<T> B(p, r - 1);
    int i, j;
    for (i = p; i < q; i++)
        B[i] = A[i];
    // Now i=q
    for (j = r; i < r; i++)
        B[--j] = A[i];
    i = p;
    j = r - 1;
    for (int k = p; k < r; k++)
        A[k] = (B[i] < B[j]) ? B[i++] : B[j--];
}

我尝试像这样在 java 中实现:

import java.util.Arrays;

public class Mergesort {

    private static int[] A = new int[]{ 4, 2, 1, 8, 6 };

    public static void main(String[] args) {
        merge_sort(0, A.length - 1);
        System.out.println(Arrays.toString(A));
    }

    public static void merge_sort(int p, int r) {
        if (p < r - 1) {
            int q = Math.floor((p + r) / 2);
            merge_sort(p, q);
            merge_sort(q + 1, r);
            merge(p, q, r);
        }
    }

    public static void merge(int p, int q, int r) {
        int[] B = new int[r - p];
        int i, j;
        for (i = p; i < q; i++)
            B[i] = A[i]
        for (j = r; i < r; i++)
            B[--j] = A[i];
        i = p;
        j = r - 1;
        for (int k = p; k < r; k++)
            A[k] = (B[i] < B[j])? B[i++] : B[j--];
    }
}

【问题讨论】:

    标签: java arrays sorting mergesort


    【解决方案1】:

    您的代码中存在多个问题:

    • 临时数组太短:因为r是最后一个元素的索引,所以大小应该是r - p + 1。将r 作为索引传递到要排序的切片的最后一个元素之后要简单得多。
    • 第一个 for 循环不正确:您应该对 BA 使用不同的索引。
    • 第二个for循环向下复制到B[r - 1],但它应该使用B[r - p]
    • 合并循环不正确:在访问B[i] 和/或B[j] 之前,您应该测试ij 是否仍在各自的边界内。
    • [次要] java 中不需要int q = Math.floor((p + r) / 2);,因为pr 的类型为int,因此除法将使用整数运算。

    这是修改后的版本:

    public class Mergesort {
    
        private static int[] A = new int[]{ 4, 2, 1, 8, 6 };
    
        public static void main(String[] args) {
            merge_sort(0, A.length);
            System.out.println(Arrays.toString(A));
        }
    
        public static void merge_sort(int p, int r) {
            if (r - p >= 2) {
                int q = p + (r - p) / 2;
                merge_sort(p, q);
                merge_sort(q, r);
                merge(p, q, r);
            }
        }
    
        public static void merge(int p, int q, int r) {
            int m = q - p;  // zero based index of the right half
            int n = r - p;  // length of the merged slice
            int[] B = new int[n];
            int i, j, k;
            for (i = p, j = 0; j < m; j++)
                B[j] = A[i++];
            for (i = r, j = m; j < n; j++)
                B[j] = A[--i];
            for (i = 0, j = n, k = p; k < r; k++) {
                // for stable sorting, i and j must be tested against their boundary
                // A[k] = (i < m && (j <= m || B[i] <= B[j - 1])) ? B[i++] : B[--j];
                // stability is not an issue for an array of int
                A[k] = (B[i] <= B[j - 1]) ? B[i++] : B[--j];
            }
        }
    }
    

    反转后半部分允许更简单的合并循环而无需边界测试。但请注意,有一种更简单的方法使用更少的内存并且可能更有效:

        public static void merge(int p, int q, int r) {
            int m = q - p;  // length of the left half
            int[] B = new int[m];
            int i, j, k;
            // only save the left half
            for (i = p, j = 0; j < m; j++)
                B[j] = A[i++];
            for (i = 0, j = q, k = p; i < m; k++) {
                A[k] = (j >= r || B[i] <= A[j]) ? B[i++] : A[j++];
            }
        }
    

    【讨论】:

    • 谢谢!是否真的有必要将逻辑运算符添加到A[k] = (i &lt; m &amp;&amp; (j &lt;= m || B[i] &lt;= B[j - 1])) ? B[i++] : B[--j]; 我用A[k] = ( B[i] &lt;= B[j - 1]) ? B[i++] : B[--j]; 测试并为到目前为止的每个实例工作,我看不出如果一个索引运行“进入另一个”应该是问题的原因
    • @FelixOuttaSpace: 好吧,通过删除边界上的测试,您会失去稳定性,但对于 int 值,在 java 中并不重要。这只是假设p &lt; r 并且似乎是扭转下半场的目的。
    猜你喜欢
    • 2019-01-27
    • 1970-01-01
    • 2020-09-12
    • 2012-03-31
    • 2021-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多