• [剑指offer] 数组中的逆序对


    剑指 Offer 51. 数组中的逆序对
    在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
    示例 1:
    输入: [7,5,6,4]
    输出: 5

    限制:
    0 <= 数组长度 <= 50000

    思路

    • 暴力解法,两个循环,依次比较,时间复杂度超了
    • 归并排序:思想,先拆散,将待排序的数组从中间递归的拆分为两块,知道最小的块长度为1;再合并,合并左边,右边,两块有序对合并。
    import org.junit.jupiter.api.Test;
    
    public class Solution {
        public int reversePairs(int[] nums) {
            int len = nums.length;
    
            if (len < 2) {//长度为0 or 1, 没有逆序对,直接返回0
                return 0;
            }
    
            int[] copy = new int[len];//copy辅助数组,非原地操作
            for (int i = 0; i < len; i++) {
                copy[i] = nums[i];
            }
    
            int[] temp = new int[len];//temp排序用到辅助数组,每次排序都使用到同一个数组,只是用到的长度不一样,避免大量新建和销毁长短不一的排序数组的开销
            return reversePairs(copy, 0, len - 1, temp);//对copy数组进行归并排序,范围从第一位到最后一位,辅助数组尾temp
        }
    
        private int reversePairs(int[] nums, int left, int right, int[] temp) {
            if (left == right) {//左右相等,说明最后两块数组也合并了。
                return 0;
            }
    
            int mid = left + (right - left) / 2;//避免溢出的写法
            int leftPairs = reversePairs(nums, left, mid, temp);//统计左边逆序对
            int rightPairs = reversePairs(nums, mid + 1, right, temp);//统计右边逆序对
    
            if (nums[mid] <= nums[mid + 1]) {//nums[mid]为左边有序数组的最大的一位,mid+1为右边有序对最小的一块,最后一部合并不额外产生逆序对,直接返回二者和
                return leftPairs + rightPairs;
            }
    
            int crossPairs = mergeAndCount(nums, left, mid, right, temp);//当左右两块均合并完成后,最后一部合并的时候的逆序对计数
            return leftPairs + rightPairs + crossPairs; //总逆序对数
        }
    
        /**
         * 最后合并两个有序数组的时候产生的逆序对数量
         * @param nums 合并后的有序对
         * @param left 左边边界
         * @param mid
         * @param right 右边界
         * @param temp 辅助排序的数组
         * @return 返回逆序对数量
         */
        private int mergeAndCount(int[] nums, int left, int mid, int right, int[] temp) {
            for (int i = left; i <= right; i++) {
                temp[i] = nums[i];
            }
    
            int i = left;//左有序第一位
            int j = mid + 1;//右有序第一位
    
            int count = 0;
            for (int k = left; k <= right; k++) {
    
                if (i == mid + 1) {
                    nums[k] = temp[j];
                    j++;
                } else if (j == right + 1) {
                    nums[k] = temp[i];
                    i++;
                } else if (temp[i] <= temp[j]) {
                    nums[k] = temp[i];
                    i++;
                } else {
                    nums[k] = temp[j];
                    j++;
                    count += (mid - i + 1);
                }
            }
            return count;
        }
    
        @Test
        public void testCase(){
            int[] arr = {7, 5, 6, 4};
            System.out.println(reversePairs(arr));
        }
    }
    
    日积月累,水滴石穿
  • 相关阅读:
    四个通用分页存储过程(来源于www.codeproject.com)
    C#2.0 print winform 打印窗体数据试试
    C# 2.0 Graphics 画雪人
    [转]Asp.Net 上传大文件专题(1)概述:上传大文件的难点
    [转]Asp.Net 上传大文件专题(2)页面生成流程
    [转]C# 调用sql 2000存储过程
    [转]Asp.Net 上传大文件专题
    [转]Asp.Net 上传大文件专题(3)从请求流中获取数据并保存为文件[下]
    c#中调用Ffmpeg转换视频格式的问题
    [转]Asp.Net 上传大文件专题(3)从请求流中获取数据并保存为文件[上]
  • 原文地址:https://www.cnblogs.com/lonelyisland/p/14469048.html
Copyright © 2020-2023  润新知