归并排序解决小和和逆序对问题

小和问题和逆序对问题是归并排序的算法的延伸应用,这篇博客将实现小和问题和逆序对问题的求解

小和问题

  • 在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和,求一个数组的小和
  • 时间复杂度 O(N*logN),空间复杂度 O(N)

代码示例

import java.util.Arrays;

/**
 * @author 民宿
 */
public class SmallSum {
    public static void main(String[] args) {
        int[] arr = new int[]{1, 3, 4, 2, 5};
        int res = smallSum(arr);
        System.out.println(res);
        System.out.println(Arrays.toString(arr));
    }

    /**
     * 小和
     *
     * @param arr 数组
     * @return
     */
    private static int smallSum(int[] arr) {
        return mergeSort(arr, 0, arr.length - 1);
    }

    /**
     * @param arr 数组
     * @param left 左指针
     * @param right  右指针
     * @return
     */
    private static int mergeSort(int[] arr, int left, int right) {
        if (left == right) {
            return 0;
        }
        int mid = left + ((right - left) >> 1);
        int lv = mergeSort(arr, left, mid);
        int rv = mergeSort(arr, mid + 1, right);
        int mv = merge(arr, left, mid, right);
        return lv + rv + mv;
    }

    /**
     * @param arr 数组
     * @param left 左指针
     * @param mid 中间指针
     * @param right 右指针
     * @return
     */
    private static int merge(int[] arr, int left, int mid, int right) {
        int cnt = 0;

        int i = left;
        int j = mid + 1;
        int[] temp = new int[right - left + 1];
        int k = 0;

        while (i <= mid && j <= right) {
            if (arr[i] < arr[j]) {
                cnt = cnt + arr[i] * (right - j + 1);
                temp[k] = arr[i];
                i++;
                k++;
            } else {
                temp[k] = arr[j];
                j++;
                k++;
            }
        }

        while (i <= mid) {
            temp[k] = arr[i];
            i++;
            k++;
        }

        while (j <= right) {
            temp[k] = arr[j];
            j++;
            k++;
        }

        for (int m = 0; m < temp.length; m++) {
            arr[m + left] = temp[m];
        }

        return cnt;
    }
}

逆序对问题

  • 在一个数组中,左边的数如果比右边的数大,则这两个数构成一个逆序对,请打印所有逆序对
  • 逆序对问题的求解与小和问题非常相似,也是归并排序的一个延伸应用

代码示例

import java.util.Arrays;

/**
 * @author 民宿
 */
public class InversionPair {
    public static void main(String[] args) {
        int[] arr = new int[]{1, 3, 4, 2, 5};
        inversionPair(arr);
        System.out.println(Arrays.toString(arr));
    }

    /**
     * 逆序对
     *
     * @param arr 数组
     */
    private static void inversionPair(int[] arr) {
        mergeSort(arr, 0, arr.length - 1);
    }

    /**
     * 归并排序
     *
     * @param arr   数组
     * @param left  左指针
     * @param right 右指针
     */
    private static void mergeSort(int[] arr, int left, int right) {
        if (left == right) {
            return;
        }

        int mid = left + ((right - left) >> 1);
        mergeSort(arr, left, mid);
        mergeSort(arr, mid + 1, right);
        merge(arr, left, mid, right);
    }

    /**
     * @param arr   数组
     * @param left  左指针
     * @param mid   中间指针
     * @param right 右指针
     */
    private static void merge(int[] arr, int left, int mid, int right) {
        int i = left;
        int j = mid + 1;
        int[] temp = new int[right - left + 1];
        int k = 0;

        while (i <= mid && j <= right) {
            if (arr[i] > arr[j]) {
                for (int n = i; n <= mid; n++) {
                    System.out.println(arr[n] + " " + arr[j]);
                }
                temp[k] = arr[j];
                j++;
                k++;
            } else {
                temp[k] = arr[i];
                i++;
                k++;
            }
        }

        while (i <= mid) {
            temp[k] = arr[i];
            i++;
            k++;
        }

        while (j <= right) {
            temp[k] = arr[j];
            j++;
            k++;
        }

        for (int m = 0; m < temp.length; m++) {
            arr[m + left] = temp[m];
        }
    }
}

 

posted @ 2021-12-04 12:32  民宿  阅读(51)  评论(0编辑  收藏  举报