算法4 2.2.20题解

浏览:47 发布日期:2025-10-20 22:05:08

题目要求间接排序

要求编写一个不改变数组的归并排序,它返回一个int[]数组perm,其中perm[i]的值时原数组中第i小的元素的位置。

约定:arr是待排序的数组,perm是记录排序索引的数组。

一些弯路,错误的尝试

最初看到这个题目,我只是重写了归并排序的merge方法,例如比较arr[j]arr[i],我只是改成了比较arr[perm[j]]arr[perm[i]],像下面这样:

public static int[] sort(Comparable[] arr)
{
    // 初始化perms并设置默认索引序列0,1,2,3...
    int[] perm = new int[arr.length];
    for (int i = 0; i < arr.length; i++)
    {
        perm[i] = i;
    }
    sort(arr, perm, 0, arr.length - 1);

    return perm;
}

public static void sort(Comparable[] arr, int[] perm, int lo, int hi)
{
    if (hi <= lo)
    {
        return;
    }

    int mid = lo + (hi - lo) / 2;
    sort(arr, perm, lo, mid);
    sort(arr, perm, mid + 1, hi);
    //merge(arr, perm, lo, mid, hi);
    m2(arr, perm, lo, mid, hi);
}


private static void m2(Comparable[] arr, int[] perm, int lo, int mid, int hi)
{
    int len = hi - lo + 1;

    StdOut.println("打印是否有序");
    showAfter(arr, perm, len);

    int i = lo, j = mid + 1;
    for (int k = lo; k <= hi; k++)
    {
        if (i > mid)
        {
            perm[k] = j++;
        }
        else if (j > hi)
        {
            perm[k] = i++;
        }
        else
        {
            Comparable aj = arr[perm[j]];
            Comparable ai = arr[perm[i]];
            if (less(aj, ai))
            {
                perm[k] = j++;
            }
            else
            {
                perm[k] = i++;
            }
        }
    }

    StdOut.println();

    //StdOut.println();
}

sort方法中,初始化perm很重要。

这个m2方法使用间接排序的方式对比数字:

Comparable aj = arr[perm[j]];
Comparable ai = arr[perm[i]];
if (less(aj, ai))
{
    perm[k] = j++;
}
else
{
    perm[k] = i++;
}

但是这个方法是不能正常工作的,例如arr:[M, C, N, D], 进行前2次排序后,perm是:[1, 0, 3, 2]。进行第2轮比较,i:0, j:2, M与N进行比较,由于N并不小于M,所以按m2方法的逻辑,perm[0]变为了0,这就不对了。

对于进行第一遍的归并排序,它的顺序是没问题的。我尝试使用选择排序最终排序了perm,它可以完成工作,不过明显不符合题目。因为选择排序不需要归并。

后来想到用插入排序,归并一段小数组使用插入排序,再使用插入排序归并更大数组,因为左半部分与右半部分是有效,使用插入排序,也可以提高效率:

// 使用插入排序
private static void merge(Comparable[] arr, int[] perm, int lo, int mid, int hi)
{
    int len = hi - lo + 1;

    StdOut.println("打印是否有序");
    showAfter(arr, perm, len);
    for (int i = 1; i < len; i++)
    {
        for (int j = i; j > 0 && less(arr[perm[j]], arr[perm[j-1]]); j--)
        {
            exchInt(perm, j, j-1);
        }
    }
}