你的归并排序实现有什么问题?

huangapple go评论74阅读模式
英文:

What's wrong with my merge sort implementation?

问题

// 归并排序的合并操作
private static void merge(int[] a, int left, int middle, int right) {
    int[] temp = new int[right - left + 1];        
    int leftCrawler = left, rightCrawler = middle;
    int currentIndex = 0;

    while (leftCrawler < middle && rightCrawler <= right) {
        if (a[leftCrawler] < a[rightCrawler])
            temp[currentIndex++] = a[leftCrawler++];
        else
            temp[currentIndex++] = a[rightCrawler++];
    }

    while (leftCrawler < middle)
        temp[currentIndex++] = a[leftCrawler++];

    while (rightCrawler <= right)
        temp[currentIndex++] = a[rightCrawler++];

    // 将 temp 复制回 a 数组
    for (int i = 0; i < temp.length; i++)
        a[left + i] = temp[i];
}

private static void mergeSort(int[] a, int left, int right) {
    if (right > left) {
        int middle = left + (right - left) / 2;
        mergeSort(a, left, middle);
        mergeSort(a, middle + 1, right);
        merge(a, left, middle, right);
    }
}

public static void mergeSort(int[] a) {
    int left = 0, right = a.length - 1;
    mergeSort(a, left, right);
}

所以我认为问题可能出在我的合并操作上然而我使用以下数组对其进行了测试
int[] a = {2, 5, 7, 15, 8, 9, 10}
以及 left = 0middle = 4 和 right = a.length - 1
合并操作成功地完成了它所需做的工作

我已经将我的归并排序实现与各种网站上的实现进行了比较但我无法找出差异所在
我的归并排序无法成功地对数组进行排序我做错了什么
英文:
// merge operation for merge sort
private static void merge(int[] a, int left, int middle, int right) {
int[] temp = new int[right - left + 1];        
int leftCrawler = left, rightCrawler = middle;
int currentIndex = 0;
while (leftCrawler &lt; middle &amp;&amp; rightCrawler &lt;= right) {
if (a[leftCrawler] &lt; a[rightCrawler])
temp[currentIndex++] = a[leftCrawler++];
else
temp[currentIndex++] = a[rightCrawler++];
}
while (leftCrawler &lt; middle)
temp[currentIndex++] = a[leftCrawler++];
while (rightCrawler &lt;= right)
temp[currentIndex++] = a[rightCrawler++];
// copy temp into a
for (int i = 0; i &lt; temp.length; i++)
a[i] = temp[i];
}
private static void mergeSort(int[] a, int left, int right) {
if (right &gt; left) {
int middle = left + (right - left) / 2;
mergeSort(a, left, middle);
mergeSort(a, middle + 1, right);
merge(a, left, middle, right);
}
}
public static void mergeSort(int[] a) {
int left = 0, right = a.length - 1;
mergeSort(a, left, right);
}

So, I thought that the issue might be with my merge operation, however I tested it on the following array int[] a = {2, 5, 7, 15, 8, 9, 10} with left = 0, middle = 4 and right = a.length - 1 and the merge operation does what it needs to successfully.

I have compared my implementation of mergeSort to those on various websites and I can't spot a difference. My mergeSort will not successfully sort the array. What am I doing wrong?

答案1

得分: 1

问题可能出在你的复制代码上:

    // 将 temp 复制到 a
    for(int i = 0; i < temp.length; i++)
        a[i] = temp[i];

你可能想要的是(注意 left + i):

    // 将 temp 复制到 a
    for(int i = 0; i < temp.length; i++)
        a[left + i] = temp[i];

(你的测试未检测出这个问题,因为 left 是 0。)

英文:

The problem might be with your copy:

    // copy temp into a
    for(int i = 0; i &lt; temp.length; i++)
        a[i] = temp[i];

What you probably want is (notice the left + i):

    // copy temp into a
    for(int i = 0; i &lt; temp.length; i++)
        a[left + i] = temp[i];

(Your test did not detect the issue, since left was 0.)

答案2

得分: 1

在你的代码中有 3 处错误:

  • 合并循环未包括位于 a[middle] 的元素,因为你使用了 leftCrawler &lt; middle,而应改为:

      while (leftCrawler &lt;= middle &amp;&amp; rightCrawler &lt;= right)
    
  • 第二个循环 while (leftCrawler &lt; middle) 也必须改为:

      while (leftCrawler &lt;= middle)
    
  • temp 复制回 a 的循环在 a 中使用了错误的索引。应为:

          // 将 temp 复制回 a
    for (int i = 0; i &lt; temp.length; i++)
    a[left + i] = temp[i];
    

注意,第一个错误源于此处使用了有害的惯例,其中 rightmiddle 包含在切片中,而不是排除在外。排除右边界可以使代码更简单,无需任何容易出错的 +1/-1 调整。

这是修改后的版本:

// 用于归并排序的合并操作
private static void merge(int[] a, int left, int middle, int right) {
    int[] temp = new int[right - left];        
    int leftCrawler = left, rightCrawler = middle;
    int currentIndex = 0;

    while (leftCrawler &lt;= middle &amp;&amp; rightCrawler &lt; right) {
        if (a[leftCrawler] &lt; a[rightCrawler])
            temp[currentIndex++] = a[leftCrawler++];
        else
            temp[currentIndex++] = a[rightCrawler++];
    }

    while (leftCrawler &lt;= middle)
        temp[currentIndex++] = a[leftCrawler++];

    while (rightCrawler &lt; right)
        temp[currentIndex++] = a[rightCrawler++];

    // 将 temp 复制回 a
    for (int i = 0; i &lt; temp.length; i++)
        a[left + i] = temp[i];
}

private static void mergeSort(int[] a, int left, int right) {
    if (right - left &gt;= 2) {
        int middle = left + (right - left) / 2;
        mergeSort(a, left, middle);
        mergeSort(a, middle, right);
        merge(a, left, middle, right);
    }
}

public static void mergeSort(int[] a) {
    mergeSort(a, 0, a.length);
}
英文:

There are 3 errors in your code:

  • the merge loop does not include the element at a[middle] because you use leftCrawler &lt; middle instead of:

      while (leftCrawler &lt;= middle &amp;&amp; rightCrawler &lt;= right)
    
  • the second loop while (leftCrawler &lt; middle) must also be changed to:

      while (leftCrawler &lt;= middle)
    
  • the loop to copy from temp back to a uses an incorrect index into a. It should be:

          // copy temp into a
    for (int i = 0; i &lt; temp.length; i++)
    a[left + i] = temp[i];
    

Note that the first error is rooted in the noxious convention used here where right and middle are included in the slices instead of excluded. Excluding the right boundaries allows for simpler code without any error prone +1/-1 adjustments.

Here is a modified version:

// merge operation for merge sort
private static void merge(int[] a, int left, int middle, int right) {
    int[] temp = new int[right - left];        
    int leftCrawler = left, rightCrawler = middle;
    int currentIndex = 0;

    while (leftCrawler &lt; middle &amp;&amp; rightCrawler &lt; right) {
        if (a[leftCrawler] &lt; a[rightCrawler])
            temp[currentIndex++] = a[leftCrawler++];
        else
            temp[currentIndex++] = a[rightCrawler++];
    }

    while (leftCrawler &lt; middle)
        temp[currentIndex++] = a[leftCrawler++];

    while (rightCrawler &lt; right)
        temp[currentIndex++] = a[rightCrawler++];

    // copy temp into a
    for (int i = 0; i &lt; temp.length; i++)
        a[left + i] = temp[i];
}

private static void mergeSort(int[] a, int left, int right) {
    if (right - left &gt;= 2) {
        int middle = left + (right - left) / 2;
        mergeSort(a, left, middle);
        mergeSort(a, middle, right);
        merge(a, left, middle, right);
    }
}

public static void mergeSort(int[] a) {
    mergeSort(a, 0, a.length);
}

huangapple
  • 本文由 发表于 2020年9月5日 23:35:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/63755647.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定