分治-归并


1. 排序数组(912)

题目描述:
在这里插入图片描述

算法原理:
这题使用归并排序的方法可以直接解决,至于归并排序的原理就是不断地去将数组一分为二,之后将分隔地不同层次的数组两两合一。
代码如下:

class Solution {
    public int[] sortArray(int[] nums) {
        mergeSort(nums, 0, nums.length - 1);
        return nums;
    }

    private void mergeSort(int[] nums, int left, int right) {
        if (left >= right) {
            return;
        }

        int mid = (left + right) / 2;
        mergeSort(nums, left, mid);
        mergeSort(nums, mid + 1, right);

        int cur1 = left, cur2 = mid + 1;
        int[] temp = new int[right - left + 1];
        int i = 0;
        while (cur1 <= mid && cur2 <= right) {
            temp[i++] = (nums[cur1] <= nums[cur2]) ? nums[cur1++] : nums[cur2++];
        }

        while (cur1 <= mid) {
            temp[i++] = nums[cur1++];
        }

        while (cur2 <= right) {
            temp[i++] = nums[cur2++];
        }

        for (int j = left; j <= right; j++) {
            nums[j] = temp[j - left];
        }
    }
}

题目链接

2. 交易逆序对的总数(LCR 170)

题目描述:
在这里插入图片描述

算法原理:
这一题要我们去求数组中的逆序对的数量,假设我们将数组分为两部分,我们可以分别求出两部分中的逆序对数量之后再去求两个部分之间交互的逆序对数量。不难发现这个过程在宏观上是与归并排序类似的,归并排序也会将数组分为两个部分,我们可以先求出这两个部分的逆序对数量之后再去利用最后合并两个数组的过程来求得最后的两个部分交互的逆序对数量。
代码如下:

class Solution {
    public int reversePairs(int[] record) {
        int n = record.length;
        return mergeSort(record, 0, n - 1);
    }

    public int mergeSort(int[] arr, int left, int right) {
        if (left >= right) {
            return 0;
        }

        int ret = 0;
        int mid = (right + left) / 2;

        ret += mergeSort(arr, left, mid);
        ret += mergeSort(arr, mid + 1, right);

        int cur1 = left, cur2 = mid + 1, i = 0;
        int temp[] = new int[right - left + 1];
        while (cur1 <= mid && cur2 <= right) {
            if (arr[cur1] > arr[cur2]) {
                ret += (right - cur2 + 1);
                temp[i++] = arr[cur1++];
            } else {
                temp[i++] = arr[cur2++];
            }
        }

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

        while (cur2 <= right) {
            temp[i++] = arr[cur2++];
        }

        for (int j = left; j <= right; j++) {
            arr[j] = temp[j - left];
        }
        return ret;
    }
}

题目链接

3. 计算右侧小于当前元素的个数(315)

题目描述:
在这里插入图片描述

算法原理:
这一题要求去求数组nums中每个元素的右侧小于该元素的个数,实际上就是求元素逆序对的个数,只不过要将对应个数保存到相应数组,做法和上一题是类似的,只不过因为最终是要经过归并排序的,数组nums中元素顺序要被打乱,所以需要建立一个数组来保存nums数组中的元素与下标之间的对应关系。
代码如下:

class Solution {
    int n;
    int[] ret;
    int[] index;
    int[] temp;
    int[] tempIndex;

    public List<Integer> countSmaller(int[] nums) {
        n = nums.length;
        ret = new int[n];
        index = new int[n];
        temp = new int[n];
        tempIndex = new int[n];
        for (int i = 0; i < n; i++) {
            index[i] = i;
        }
        mergeSort(nums, 0, n - 1);

        List<Integer> list = new ArrayList<>();
        for (int x : ret) {
            list.add(x);
        }
        return list;
    }

    public void mergeSort(int[] nums, int left, int right) {
        if (left >= right) {
            return;
        }

        int mid = (left + right) / 2;
        mergeSort(nums, left, mid);
        mergeSort(nums, mid + 1, right);

        int cur1 = left, cur2 = mid + 1;
        int i = 0;
        while (cur1 <= mid && cur2 <= right) {
            if (nums[cur1] > nums[cur2]) {
                ret[index[cur1]] += (right - cur2 + 1);
                temp[i] = nums[cur1];
                tempIndex[i++] = index[cur1++];
            } else {
                temp[i] = nums[cur2];
                tempIndex[i++] = index[cur2++];
            }
        }

        while (cur1 <= mid) {
            temp[i] = nums[cur1];
            tempIndex[i++] = index[cur1++];
        }

        while (cur2 <= right) {
            temp[i] = nums[cur2];
            tempIndex[i++] = index[cur2++];
        }

        for (int j = left; j <= right; j++) {
            nums[j] = temp[j - left];
            index[j] = tempIndex[j - left];
        }
    }
}

题目链接

4. 翻转对(493)

题目描述:
在这里插入图片描述

算法原理:
这里与前面两题的区别是求得逆序对需要满足大于两倍这种条件,因此无法与归并的过程结合,需要先计算出逆序对的数量之后再进行归并。
代码如下:

class Solution {
    int[] temp;

    public int reversePairs(int[] nums) {
        int n = nums.length;
        temp = new int[n];
        return mergeSort(nums, 0, n - 1);
    }

    public int mergeSort(int[] nums, int left, int right) {
        if (left >= right) {
            return 0;
        }

        int ret = 0;
        int mid = (right + left) / 2;
        ret += mergeSort(nums, left, mid);
        ret += mergeSort(nums, mid + 1, right);

        int cur1 = left, cur2 = mid + 1, i = 0;

        while (cur2 <= right) {
            while (cur1 <= mid && nums[cur1] / 2.0 <= nums[cur2]) {
                cur1++;
            }
            if (cur1 > mid) {
                break;
            }
            ret += (mid-cur1 + 1);
            cur2++;
        }

        cur1 = left;
        cur2 = mid + 1;

        while (cur1 <= mid && cur2 <= right) {
            if (nums[cur1] <= nums[cur2]) {
                temp[i++] = nums[cur1++];
            } else {
                temp[i++] = nums[cur2++];
            }
        }

        while (cur1 <= mid) {
            temp[i++] = nums[cur1++];
        }

        while (cur2 <= right) {
            temp[i++] = nums[cur2++];
        }

        for (int j = left; j <= right; j++) {
            nums[j] = temp[j - left];
        }

        return ret;
    }
}

题目链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值