剑指offer Leetcode 51. 数组中的逆序对

image-20201215201714298

解法1:暴力法(超时)

思想:

​ 每次固定一个节点,遍历其后的节点

复杂度:

​ ●时间:O(N^2)

​ ●空间:O(1)

代码:
class Solution {
public:
    int reversePairs(vector<int>& nums) {
        if(nums.size() <= 1)
            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;
    }
};

解法2:归并排序

思想:

​ 在归并排序中加入求逆序对。

​ 求一个区间的逆序对 = 左区间内部逆序对 + 右区间内部逆序对 + 跨左右区间的逆序对

image-20201215213856863
复杂度:

和归并排序的复杂度一样
image-20201215214529454
​ ●时间:O(NlogN)

​ ●空间:O(N),额外数组

代码:
class Solution {
private:
    // 不能在这里定义,因为vector<int>tmp(5, 0)也可以看做是声明一个返回vector<int>的tmp函数
    vector<int>tmp;
public:
    int reversePairs(vector<int>& nums) {
        // 在函数内定义tmp
        tmp.assign(nums.size(), 0);
        return merge_sort(nums, 0, nums.size() - 1);
    }
    // 返回逆序对总数的归并排序
    int merge_sort(vector<int>& nums, int left, int right){
        if(left >= right)
            return 0;
        int mid = left + (right - left) / 2;
        // 此时左右两个区间均排序完毕,且ans为左区间和右区间的逆序对总数(左内部 + 右内部,没加上跨区间的逆序对),再加上两个区间各出一个数的逆序对数就得到最终结果
        int ans = merge_sort(nums, left, mid) + merge_sort(nums, mid + 1, right);
        int left_index = left, right_index = mid + 1;
        int tmp_index = 0;

        // 下面三个while就是一般的归并排序,不同的是第一个while的else
        while(left_index <= mid && right_index <= right){
            if(nums[left_index] <= nums[right_index])
                tmp[tmp_index++] = nums[left_index++];
            else{
                //从右边拿数到tmp,说明左边数组剩余的数都大于此数,并且位置在此数的左边,所以左边剩余多少数就构成多少逆序对
                ans += mid - left_index + 1;
                tmp[tmp_index++] = nums[right_index++];
            }
        }
        while(left_index <= mid){
            tmp[tmp_index++] = nums[left_index++];
        }
        while(right_index <= right){
            tmp[tmp_index++] = nums[right_index++];
        }
        //从临时数组赋值到nums中,排序需要改变nums数组
        tmp_index = 0;
        for(int i = left; i <= right; ++i){
            nums[i] = tmp[tmp_index++];
        }
        return ans;
    }
};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值