归并排序面试题

138 阅读3分钟

「这是我参与2022首次更文挑战的第29天,活动详情查看:2022首次更文挑战」。

一、小和问题

在一个数组中,一个数左边比它小的数的总和,叫数的小和,所有数的小和累加起来,叫数组小和。求数组小和。 例子: [1,3,4,2,5] 1左边比1小的数:没有 3左边比3小的数:1 4左边比4小的数:1、3 2左边比2小的数:1 5左边比5小的数:1、3、4、 2 所以数组的小和为1+1+3+1+1+3+4+2=16

1、分析

小和问题:每个数左边比自己小的数都累加起来,最后整体累加起来

image.png

方法一:当来到每个位置,遍历前边所有的数,比自己小的累加起来,左后全部累计起来,时间复杂度:O(N²)

方法二:换个思路,当前i来到a位置,右边有多少个比a大,就产生多少个a(x个a),当前i来到b位置,右边有多少个比b大,就产生多少个b(y个b),当前i来到c位置,右边有多少个比c大,就产生多少个c(z个c),最后小和累加 = x*a + y*b + z*c

左边每个数找一遍比自己小的数 = 右边有多少个数比自己大

2、实现

public static int smallSum(int[] arr) {
    if (arr == null || arr.length < 2) {
        return 0;
    }
    return process(arr, 0, arr.length - 1);
}

// arr[L..R]既要排好序,也要求小和返回
// 所有merge时,产生的小和,累加
// 左 排序   merge
// 右 排序  merge
// merge
public static int process(int[] arr, int L, int R) {
    if (L == R) {
        return 0;
    }
    // l < r
    int mid = L + ((R - L) >> 1);
    return
        process(arr, L, mid)
        +
        process(arr, mid + 1, R)
        +
        merge(arr, L, mid, R);
}

public static int merge(int[] arr, int L, int M, int R) {
    int[] help = new int[R - L + 1];
    int i = 0;
    int p1 = L;
    int p2 = M + 1;
    int res = 0; // 核心代码
    while (p1 <= M && p2 <= R) {
        res += arr[p1] < arr[p2] ? (R - p2 + 1) * arr[p1] : 0; // 核心代码
        help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
    }
    while (p1 <= M) {
        help[i++] = arr[p1++];
    }
    while (p2 <= R) {
        help[i++] = arr[p2++];
    }
    for (i = 0; i < help.length; i++) {
        arr[L + i] = help[i];
    }
    return res; // 核心代码
}

二、逆序对问题

在一个数组中, 任何一个前面的数a,和任何一个后面的数b, 如果(a,b)是降序的,就称为逆序对 返回数组中所有的逆序对

1、分析

任何一个左边的数和任何一个右边的数是降序的,就称为逆序对

可以转化为右边有多少个数比它小问题,求x,右边有多少个比x小的逆序对,只有x作为左手姿态,右边才有可能产生逆序对,若x作为右手姿态,不可能有逆序对

核心思路:merge过程,从右往左拷贝,两个指针开始分别指向最右位置,谁大拷贝谁,相等右边指针向前移动

image.png

2、实现

public static int reversePairNumber(int[] arr) {
    if (arr == null || arr.length < 2) {
        return 0;
    }
    return process(arr, 0, arr.length - 1);
}

// arr[L..R]既要排好序,也要求逆序对数量返回
// 所有merge时,产生的逆序对数量,累加,返回
// 左 排序 merge并产生逆序对数量
// 右 排序 merge并产生逆序对数量
public static int process(int[] arr, int L, int R) {
    if (L == R) {
        return 0;
    }
    // l < r
    int mid = L + ((R - L) >> 1);
    return process(arr, L, mid) + process(arr, mid + 1, R) + merge(arr, L, mid, R);
}

public static int merge(int[] arr, int L, int M, int R) {
    int[] help = new int[R - L + 1];
    int i = help.length - 1;
    int p1 = M;
    int p2 = R;
    int res = 0;
    while (p1 >= L && p2 > M) {
        res += arr[p1] > arr[p2] ? (p2 - M) : 0;
        help[i--] = arr[p1] > arr[p2] ? arr[p1--] : arr[p2--];
    }
    while (p1 >= L) {
        help[i--] = arr[p1--];
    }
    while (p2 > M) {
        help[i--] = arr[p2--];
    }
    for (i = 0; i < help.length; i++) {
        arr[L + i] = help[i];
    }
    return res;
}

三、总结

小和问题:

  • 在归并排序中的merge过程中求小和
  • 转换思路

逆序对问题:

  • 归并排序merge过程进一步体验

两个题都用到了归并排序,前提需要掌握归并排序的过程,这两道题都是在mergeSort过程中做手脚。

友情链接:归并排序