【算法从零到千】【51-54】逆序对(分治+递归算法)


1. 排序数组

912. 排序数组https://leetcode.cn/problems/sort-an-array/

给你一个整数数组 nums,请你将该数组升序排列。

你必须在 不使用任何内置函数 的情况下解决问题,时间复杂度为 O(nlog(n)),并且空间复杂度尽可能小。

cpp 复制代码
class Solution {
public:
    vector<int> tmp;
    vector<int> sortArray(vector<int>& nums) {
        tmp.resize(nums.size());
        merge_sort(nums,0,nums.size()-1);
        return nums;
    }
    void merge_sort(vector<int>& nums,int left,int right)
    {
        if(left>=right) return;
        int mid = left + ((right - left) >> 1)

        merge_sort(nums,left,mid);
        merge_sort(nums,mid+1,right);

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

2. 数组逆序对的个数

LCR 170. 交易逆序对的总数https://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-dui-lcof/

在股票交易中,如果前一天的股价高于后一天的股价,则可以认为存在一个「交易逆序对」。请设计一个程序,输入一段时间内的股票交易记录 record,返回其中存在的「交易逆序对」总数

cpp 复制代码
class Solution {
public:
    vector<int> tmp;
    int reversePairs(vector<int>& nums) {
        tmp.resize(nums.size());
        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) >> 1);
        int ret=merge_sort(nums,left,mid)+merge_sort(nums,mid+1,right);

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

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

315. 计算右侧小于当前元素的个数https://leetcode.cn/problems/count-of-smaller-numbers-after-self/

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

cpp 复制代码
class Solution {
public:
// (1)在求逆序对中,使用归并排序,是计算"左侧大于当前元素的总个数"
// 在比较的时候, 当 nums[i] > nums[j] 时, 说明 i ~ mid 的这些数,都是大于nums[j]的,
//         所以总数 += mid-i+1
// (2)在本题中,首先,我们先想,计算"右侧小于当前元素的总个数"(先不管每个数右边详细小于的个数)
// 在比较的时候, 
//    当 nums[i] > nums[j] 时,先不管,j往后走;
//    当 nums[i] <=nums[j] 时,说明 mid+1 ~ j-1 的这些数,都是小于nums[i]的,
//         所以总数 += j-1 - (mid+1) +1,即总数 += j-mid-1
// 然后,因为本题要具体到,每个数右边详细的小于个数,
// 所以,可以在一开始,使用pair,对 nums 里的数附加一个索引,变成num_index,记录数的值和数对应的下标位置,
// 然后,就可以在添加时,具体到这个数对应的位置了: (pair中,first为num的值,second为num的下标)
//    当 num_index[i].first <= num_index[j].first ,说明 mid+1 ~ j-1 的这些数,都是小于nums[i]的,
//           对应下标的个数增加: count[num_index[i].second] += j-mid-1
    vector<pair<int, int>> temp;
    vector<int> count;
    vector<int> countSmaller(vector<int>& nums) {
        int n = nums.size();
        vector<pair<int, int>> num_index;
        for (int i = 0; i < n; i++)
            num_index.push_back(pair<int, int>(nums[i], i));
        
        temp = vector<pair<int, int>>(n);
        count = vector<int>(n, 0);

        merge_sort(num_index, 0, n-1);
        return count;
    }
    void merge_sort(vector<pair<int, int>>& num_index, int l, int r){
        if (l >= r) return;
        int mid = l + (r - l) / 2;
        merge_sort(num_index, l, mid);
        merge_sort(num_index, mid+1, r);
        merge(num_index, l, mid, r);
    }
    void merge(vector<pair<int, int>>& num_index, int l, int mid, int r){
        int i = l, j = mid + 1;
        int k = l;
        while (i <= mid && j <= r){
            // 当 num_index[i].first <= num_index[j].first 时,说明 mid+1 ~ j-1的这些数,都是小于nums[i]的,
            // 所以count[num_index[i].second] += j-mid-1
            if (num_index[i].first <= num_index[j].first){
                count[num_index[i].second] += j - mid - 1;
                temp[k++] = num_index[i++];
            }
            else temp[k++] = num_index[j++];
        }
        while (i <= mid) { // 如果右边的j走完了,现在的i开始的数,其实都是大于所有右边的数的
            count[num_index[i].second] += j - mid - 1; 
            temp[k++] = num_index[i++];
        }
        while (j <= r) temp[k++] = num_index[j++];
        for (i = l; i <= r; i++)
            num_index[i] = temp[i];
    }
};

4. 翻转对

493. 翻转对https://leetcode.cn/problems/reverse-pairs/

给定一个数组 nums ,如果 i < jnums[i] > 2*nums[j] 我们就将 (i, j) 称作一个重要翻转对

你需要返回给定数组中的重要翻转对的数量

cpp 复制代码
class Solution {
public:
    vector<int> tmp;
    int reversePairs(vector<int>& nums) {
        tmp.resize(nums.size());
        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) >> 1);
        int ret=merge_sort(nums,left,mid)+merge_sort(nums,mid+1,right);

        int cur1=left,cur2=mid+1,i=0;
        while(cur2<=right)
        {
            while(cur1<=mid&&nums[cur1] <= 2 * (long long)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])
            tmp[i++]=nums[cur2++];
            else
            tmp[i++]=nums[cur1++];
        }
        
        while(cur1<=mid)
        tmp[i++]=nums[cur1++];   
        while(cur2<=right)
        tmp[i++]=nums[cur2++];
        for(int i=left;i<=right;i++)
        nums[i]=tmp[i-left];
        return ret;
    }
};

相关推荐
小星星闪亮登场1 小时前
2026河南萌新联赛第四场--南阳理工学院
数据结构·算法·贪心算法·动态规划·哈希算法·广度优先
土司大王1 小时前
LeetCode hot100——两数之和
数据结构·算法·leetcode
luj_17681 小时前
大航海时代:沉浸式财商实战沙盒
c语言·开发语言·网络·经验分享·算法
Navigator_Z2 小时前
LeetCode //C - 1200. Minimum Absolute Difference
c语言·算法·leetcode
wabs6662 小时前
关于字符串【力扣344.反转字符串的思考】
数据结构·算法·leetcode
LuminousCPP2 小时前
数据结构基础篇(二):顺序表与链表全方位对比|从内存布局到 CPU 缓存理解底层差异
c语言·数据结构·经验分享·链表·缓存
有点。3 小时前
C++二叉树(一)
开发语言·数据结构·c++
民乐团扒谱机3 小时前
【微实验】谐波乘积谱(HPS)算法深度解析:原理、数学与代码实现
开发语言·人工智能·python·算法·语音识别·音乐
Nil2083 小时前
leetcode 73矩阵置0
算法·leetcode·矩阵