归并排序流程

3. 归并排序相关的算法 - 图1

1. 归并排序递归实现

代码实现:

  1. public class Mine_MergeSort {
  2. // 归并排序 递归实现方式
  3. public static void mergeSort(int[] arr) {
  4. if (arr == null || arr.length < 2) {
  5. return;
  6. }
  7. process(arr, 0, arr.length - 1);
  8. }
  9. // 归并排序的递归过程
  10. // 递归含义,数组L-R范围上实现有序
  11. public static void process(int[] arr, int L, int R) {
  12. if (L == R) {
  13. return;
  14. }
  15. // 分治思想 L-mid 左边有序, mid+1 - R右侧有序,然后两个有序数组进行merge整体有序
  16. int mid = L + ((R - L) >> 1);
  17. process(arr, L, mid);
  18. process(arr, mid + 1, R);
  19. merge(arr, L, mid, R);
  20. }
  21. // merge过程,合并两个有序数组
  22. public static void merge(int[] arr, int L, int M, int R) {
  23. //双指针 + 辅助数组
  24. int[] help = new int[R - L + 1];
  25. int i = 0;
  26. int p1 = L;
  27. int p2 = M + 1;
  28. //merge
  29. while (p1 <= M && p2 <= R) {
  30. // 谁小拷贝谁,相等先拷贝左边
  31. help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
  32. }
  33. while (p1 <= M) {
  34. help[i++] = arr[p1++];
  35. }
  36. while (p2 <= R) {
  37. help[i++] = arr[p2++];
  38. }
  39. //将辅助数组中的值拷贝到arr数组中
  40. for (int j = 0; j < help.length; j++) {
  41. arr[L + j] = help[j];
  42. }
  43. }
  44. // 测试
  45. public static void main(String[] args) {
  46. int[] arr = {3, 4, 2, 6, 5, 3, 1, 9, 6, 5, 0};
  47. mergeSort(arr);
  48. for (int each : arr) {
  49. System.out.print(each + " ");
  50. }
  51. }
  52. }

2. 递归排序相关的题目

2.1 小和问题

小和问题

2.2. 逆序对问题

逆序对

package class04;

/**
 * 查找一个数组中有多少个逆序对
 *         就是相当于求每一个数右边有多少个数比它小
 */
public class Code03_ReversePair {

    public static int reverPairNumber(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;
    }

    // for test
    public static int comparator(int[] arr) {
        int ans = 0;
        for (int i = 0; i < arr.length; i++) {
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[i] > arr[j]) {
                    ans++;
                }
            }
        }
        return ans;
    }

    // for test
    public static int[] generateRandomArray(int maxSize, int maxValue) {
        int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) (maxValue * Math.random());
        }
        return arr;
    }

    // for test
    public static int[] copyArray(int[] arr) {
        if (arr == null) {
            return null;
        }
        int[] res = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            res[i] = arr[i];
        }
        return res;
    }

    // for test
    public static boolean isEqual(int[] arr1, int[] arr2) {
        if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) {
            return false;
        }
        if (arr1 == null && arr2 == null) {
            return true;
        }
        if (arr1.length != arr2.length) {
            return false;
        }
        for (int i = 0; i < arr1.length; i++) {
            if (arr1[i] != arr2[i]) {
                return false;
            }
        }
        return true;
    }

    // for test
    public static void printArray(int[] arr) {
        if (arr == null) {
            return;
        }
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }

    // for test
    public static void main(String[] args) {
        int testTime = 500000;
        int maxSize = 100;
        int maxValue = 100;
        System.out.println("测试开始");
        for (int i = 0; i < testTime; i++) {
            int[] arr1 = generateRandomArray(maxSize, maxValue);
            int[] arr2 = copyArray(arr1);
            if (reverPairNumber(arr1) != comparator(arr2)) {
                System.out.println("Oops!");
                printArray(arr1);
                printArray(arr2);
                break;
            }
        }
        System.out.println("测试结束");
    }

}

2.3. 右侧大于二倍问题

package class04;

public class Code04_BiggerThanRightTwice {

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

    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) {
        // [L....M]   [M+1....R]

        int ans = 0;
        // 目前囊括进来的数,是从[M+1, windowR)
        int windowR = m + 1;
        for (int i = L; i <= m; i++) {
            while (windowR <= r && arr[i] > (arr[windowR] * 2)) {
                windowR++;
            }
            ans += windowR - m - 1;
        }


        int[] help = new int[r - L + 1];
        int i = 0;
        int p1 = L;
        int p2 = m + 1;
        while (p1 <= m && p2 <= r) {
            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 ans;
    }

    // for test
    public static int comparator(int[] arr) {
        int ans = 0;
        for (int i = 0; i < arr.length; i++) {
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[i] > (arr[j] << 1)) {
                    ans++;
                }
            }
        }
        return ans;
    }

    // for test
    public static int[] generateRandomArray(int maxSize, int maxValue) {
        int[] arr = new int[(int) ((maxSize + 1) * Math.random())];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = (int) ((maxValue + 1) * Math.random()) - (int) ((maxValue + 1) * Math.random());
        }
        return arr;
    }

    // for test
    public static int[] copyArray(int[] arr) {
        if (arr == null) {
            return null;
        }
        int[] res = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            res[i] = arr[i];
        }
        return res;
    }

    // for test
    public static boolean isEqual(int[] arr1, int[] arr2) {
        if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)) {
            return false;
        }
        if (arr1 == null && arr2 == null) {
            return true;
        }
        if (arr1.length != arr2.length) {
            return false;
        }
        for (int i = 0; i < arr1.length; i++) {
            if (arr1[i] != arr2[i]) {
                return false;
            }
        }
        return true;
    }

    // for test
    public static void printArray(int[] arr) {
        if (arr == null) {
            return;
        }
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }

    // for test
    public static void main(String[] args) {
        int testTime = 500000;
        int maxSize = 100;
        int maxValue = 100;
        System.out.println("测试开始");
        for (int i = 0; i < testTime; i++) {
            int[] arr1 = generateRandomArray(maxSize, maxValue);
            int[] arr2 = copyArray(arr1);
            if (biggerTwice(arr1) != comparator(arr2)) {
                System.out.println("Oops!");
                printArray(arr1);
                printArray(arr2);
                break;
            }
        }
        System.out.println("测试结束");
    }

}

2.4 区间和个数

leetcode 327

给你一个整数数组 nums 以及两个整数 lower 和 upper 。
求数组中,值位于范围 [lower, upper] (包含 lower 和 upper)之内的 区间和的个数 。

方式一: 暴力解决, 复杂度 O(n2)

    public static int countRangeSum(int[] nums, int lower, int upper) {
        int countSize = 0;
        // 统计给定数组的子数组数
        for(int i = 0; i < nums.length; i++){
            long sum = 0;
            for (int j = i; j < nums.length; j++){
                //num[i,j]上求和
                sum += nums[j];
                if (sum >= lower && sum <= upper){
                    countSize++;
                }
            }
        }
        return countSize;
    }

方式二: 归并排序 时间复杂度 O(N * logN)

public static int countRangeSum(int[] nums, int lower, int upper) {
    if (nums == null || nums.length == 0) {
        return 0;
    }
    long[] sum = new long[nums.length];
    sum[0] = nums[0];
    for (int i = 1; i < nums.length; i++) {
        sum[i] = sum[i - 1] + nums[i];
    }
    return process(sum, 0, sum.length - 1, lower, upper);
}
public static int process(long[] sum, int L, int R, int lower, int upper) {
    if (L == R) {
        return sum[L] >= lower && sum[L] <= upper ? 1 : 0;
    }
    int M = L + ((R - L) >> 1);
    return process(sum, L, M, lower, upper) + process(sum, M + 1, R, lower, upper)
            + merge(sum, L, M, R, lower, upper);
}
public static int merge(long[] arr, int L, int M, int R, int lower, int upper) {
    int ans = 0;
    int windowL = L;
    int windowR = L;
    // [windowL, windowR)
    for (int i = M + 1; i <= R; i++) {
        long min = arr[i] - upper;
        long max = arr[i] - lower;
        while (windowR <= M && arr[windowR] <= max) {
            windowR++;
        }
        while (windowL <= M && arr[windowL] < min) {
            windowL++;
        }
        ans += windowR - windowL;
    }
    long[] help = new long[R - L + 1];
    int i = 0;
    int p1 = L;
    int p2 = M + 1;
    while (p1 <= M && p2 <= R) {
        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 ans;
}