• 面试题51:数组中的逆序对(C++)


    题目地址:https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/

    题目描述

    在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

    题目示例

    示例 1:

    输入: [7,5,6,4]
    输出: 5

    解题思路

    暴力法:最容易想到的是暴力遍历,枚举寻找可能构成逆序对的个数,发现一个则res++,但可惜的是超时。

    分治思想:

    • Step1:分解,直至剩下一个元素为止,默认长度为1的序列是已经排好序的。

    图1

    • Step2:合并,自底向上依次合并有序数组,并计算逆序对个数,直至完全合并为有序数组为止。

     图2

     图3

    图4

    归并排序+分治思想:首先,将数组nums拆分为两部分,即nums[left,mid]和nums[mid+1,right],然后使用递归函数l计算leftPairs和rightPairs两个子序列的逆序对以及归并时的逆序对crossPairs个数,最后返回三者之和leftPairs+rightPairs+crossPairs。当然,归并排序还有可以优化的地方,当我们检测到数组已经有序时,就不需要合并了,直接返回左边和右边逆序对个数即可,即leftPairs+rightPairs。

    典型的归并排序实现,其它题目可参考315、327、493.

    程序源码

    暴力法(超时

    class Solution {
    public:
        int reversePairs(vector<int>& nums) {
            if(nums.size() == 0) return 0;
            int res = 0;
            for(int i = 0; i < nums.size() - 1; i++)
            {
                for(int j = i + 1; j < nums.size(); j++)
                {
                    if(nums[i] > nums[j])  res++;
                }
            }
            return res;
        }
    };

    归并排序+分治思想

    class Solution {
    public:
        int reversePairs(vector<int>& nums) {
            if(nums.size() < 2) return 0; //无法构成逆序对
            int len = nums.size();
            vector<int> copy(len); //拷贝原始数组,因为需要一边计算逆序对的个数,一边排序,因此,算法是修改原始数组的,所以需要拷贝原始数组
            for(int i = 0; i < len; i++)
            {
                copy[i] = nums[i];
            }
            vector<int> temp(len); //辅助数组,归并两个有序数组
            return reversePairs(copy, 0, len - 1, temp); //递归计算逆序对个数
        }
        /*nums[left,right]计算逆序对个数并排序*/
        int reversePairs(vector<int>&nums, int left, int right, vector<int>&temp)
        {
            if(left == right) return 0; //递归终止条件,当left==right时,只剩下一个元素,不构成逆序对
            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]) //归并排序优化
            {
                return leftPairs + rightPairs; //若检测到数组已经有序,则不需要合并,直接返回左边和右边逆序对个数即可
            }
            int crossPairs = mergeCount(nums, left, mid, right, temp); //跨越两个区间的逆序对的计算
            return leftPairs + rightPairs + crossPairs; //总逆序对个数
        }
        /*mergetCount()计算前提是nums[left,mid]与nums[mid+1,right]均有序*/
        int mergeCount(vector<int>&nums, int left, int mid, int right, vector<int>&temp)
        {
            //拷贝nums中元素到辅助数组temp中
            for(int i = left; i <= right; i++)
            {
                temp[i] = nums[i];
            }
            int i = left; //i指向区间nums[left,mid]左边界
            int j = mid + 1; //j指向区间nums[mid+1,right]左边界
            int cnt = 0; //计数器
            for(int k = left; k <= right; k++) //确定哪个元素合并到nums中
            {
                if(i == mid + 1) 
                {
                    nums[k] == temp[j]; //左指针已经超出区间nums[left,mid],则直接将nums[mid+1,right]归并到nums数组中
                    j++;
                }
                else if(j == right + 1)
                {
                    nums[k] = temp[i]; ////右指针已经超出区间nums[mid+1,right],则直接将nums[left,mid]归并到nums数组中
                    i++;
                }
                else{ //左右指针i和j均在区间nums[left,mid]和nums[mid+1,right]中
                     if(temp[i] <= temp[j])
                    {
                    nums[k] = temp[i];
                    i++;
                    }
                    else
                    {
                    nums[k] = temp[j];
                    j++;
                    cnt += (mid - i + 1); //逆序对个数计算,即第一个数组nums[left,mid]中还未归并的元素个数,比如[5,4,3,2,1]和[4,6,7]中,左指针i指向5,右指针j指向4,比较元素5与4,发现5>4,归并元素5到nums数组中,并右指针j++指向元素6,然后计算逆序对的个数4,即[5,4,3,2,1]中还未归并的元素个数(元素4,3,2,1均未归并)
                    }
                }
            }
            return cnt;
        }
    };

    参考文章

    https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/solution/shu-zu-zhong-de-ni-xu-dui-by-leetcode-solution/

    ----------------------------------- 心之所向,素履所往;生如逆旅,一苇以航。 ------------------------------------------
  • 相关阅读:
    Python的包管理工具Pip
    C语言移位运算符
    malloc函数具体解释
    HDU
    Java中Scanner的使用方法
    DOS call 中的%cd%,当前文件夹演示
    没有找到MSVCR100.dll解决方法
    什么是响应式表格(响应式表格和普通表格的区别)
    Redis和Memcache和MongoDB简介及区别分析(整理)
    GIT将本地项目上传到Github(两种简单、方便的方法)
  • 原文地址:https://www.cnblogs.com/wzw0625/p/12824380.html
Copyright © 2020-2023  润新知