我的合并排序实现有什么问题?

问题描述

// 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 < 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++];

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

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

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

因此,我认为问题可能出在我的合并操作上,但是我在下面的数组int[] a = {2,5,7,15,8,9,10}中使用left = 0middle = 4right = a.length - 1以及合并操作成功完成了所需的工作。

我已经将我的mergeSort的实现与各种网站上的实现进行了比较,但我发现没有什么不同。我的mergeSort无法成功对数组进行排序。我在做什么错了?

解决方法

问题可能出在您的副本上:

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

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

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

(由于left为0,您的测试未检测到问题。)

,

您的代码中存在3个错误:

  • 合并循环不包含a[middle]处的元素,因为您使用leftCrawler < middle而不是:

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

      while (leftCrawler <= middle)
    
  • 要从temp复制回到a的循环使用了错误的a索引。应该是:

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

请注意,第一个错误源于此处使用的有害约定,其中切片中包含rightmiddle而不是排除在外。排除正确的边界可以简化代码,而无需进行容易出错的+1 / -1调整。

这是修改后的版本:

// 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 < 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++];

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

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

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