zoukankan      html  css  js  c++  java
  • Hard | LeetCode 315. 计算右侧小于当前元素的个数 | 归并排序

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

    给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。

    示例:

    输入:nums = [5,2,6,1]
    输出:[2,1,1,0] 
    解释:
    5 的右侧有 2 个更小的元素 (2 和 1)
    2 的右侧仅有 1 个更小的元素 (1)
    6 的右侧有 1 个更小的元素 (1)
    1 的右侧有 0 个更小的元素
    

    提示:

    • 0 <= nums.length <= 10^5
    • -10^4 <= nums[i] <= 10^4

    解题思路

    方法一: 归并排序

    此题要求, 某个数右边比当前元素小的元素的个数, 也就是找逆序对。不过逆序对还需要在第一个元素的位置进行计数。找逆序对与 Hard | 剑指 Offer 51. 数组中的逆序对 | 归并排序 类似, 要针对每个下标进行计数, 使用一个索引数组即可。

    索引数组:新建一个数组保存源数组中所有元素的下标。针对源数组中所有元素的位置变动, 源数组不做任何改动, 只变动索引数组。在需要进行大小比较时, 以索引数组的的某个值, 作为源数组的下标, 可以获得真正的值。

    然后题目是要求某个数里, 从右边找比当前元素小的个数。

    Hard | 剑指 Offer 51. 数组中的逆序对 | 归并排序 我们知道, 在找两个有序数组的逆序对时(假设为[[A],[B]]), 从左往右归并, 若ra > rb, 则说明形成了逆序对, 则ra后边的元素全部比rb 大。也就是可以得到数组里某个数组, 左边比当前元素大的部分。而要一次找出右边比当前元素小的部分, 则从左往右归并是无法一次找出的。

    解决办法是:从右往左归并, 通过这种归并顺序, 就可以一次找出右边比当前元素小的个数。

    public List<Integer> countSmaller(int[] nums) {
        // index 作为索引数组, 排序时, 需要对索引数组进行排序
        // 在排序过程当中比较的对象是索引数组的值作为原数组下标的值
        int[] index = new int[nums.length];
        for (int i = 1; i < index.length; i++) {
            index[i] = i;
        }
        int[] temp = new int[nums.length];
        int[] res = new int[nums.length];
        mergeSortCore(nums, index, 0, nums.length - 1, temp, res);
        List<Integer> resList = new ArrayList<>(res.length);
        for (int re : res) {
            resList.add(re);
        }
        return resList;
    }
    
    public void mergeSortCore(int[] nums, int[] index,  int left, int right, int[] temp, int[] res) {
        if (left >= right) {
            return;
        }
    
        // 先将数组切分成两半, 递归的归并这两个一半的数组
        int mid = (left + right) >> 1;
        mergeSortCore(nums, index, left, mid, temp, res);
        mergeSortCore(nums, index, mid + 1, right, temp, res);
        // 两个一半的数组归并完成之后, 保存nums数组的[left, right]部分到temp作为归并的辅助数组
        for (int i = left; i <= right; i++) {
            temp[i] = index[i];
        }
        // 接下来对两部分已经排序的数组做归并
        // 这里要求A数组里每个值, 找到B数组里比这个值小的数字的数量
        // 所以归并时应当从后往前归并, 将大的数放进尾部
        int aPtr = mid, bPtr = right;
        int cursor = right;
        while (aPtr >= left && bPtr > mid) {
            if (nums[temp[aPtr]] <= nums[temp[bPtr]]) {
                index[cursor--] = temp[bPtr--];
            } else {
                // aPtr与bPtr形成逆序对
                // 则说明aPtr与bPtr之前的所有数字形成逆序对
                res[temp[aPtr]] += (bPtr - mid);
                index[cursor--] = temp[aPtr--];
            }
        }
        // 其中一个数组已经归并完成, 将另一个数组的未归并部分直接拷贝
        if (aPtr < left) {
            System.arraycopy(temp, mid + 1, index, left, bPtr - mid);
        }
    }
    
  • 相关阅读:
    Euclid's Game (简单博弈)
    “科大讯飞杯”第十七届同济大学程序设计预选赛暨高校网络友谊赛(部分题解)
    牛客小白月赛4 (B 博弈论)
    博弈--尼姆博弈
    C# .net中获取台式电脑中串口设备的名称
    打印出C# 中float ,double 在内存中的存放形式
    VS2010 C++ 创建COM组件
    .net 中两个日期算经过的月份数
    一种计算MD5的实现方法
    将文件从程序集中复原
  • 原文地址:https://www.cnblogs.com/chenrj97/p/14654214.html
Copyright © 2011-2022 走看看