专题八_分治-归并

目录

912. 排序数组

解析

题解

LCR 170. 交易逆序对的总数(数组中的逆序对)

解析

题解

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

解析

题解

493. 翻转对

解析

题解


912. 排序数组

912. 排序数组

 

解析

题解

1.局部临时数组

class Solution {
public:
    vector<int> sortArray(vector<int>& nums) {
        // 49.专题八_分治-归并_归并排序_C++
        mergeSort(nums, 0, nums.size() - 1);
        return nums;
    }
    void mergeSort(vector<int>& nums, int left, int right)
    {
        if (left >= right) return;

        // 1.选择中间点划分区间
        int mid = (right - left) / 2 + left;
        // [left, mid] [mid + 1, right]

        // 2.把左右区间排序
        mergeSort(nums, left, mid);
        mergeSort(nums, mid + 1, right);

        // 3.合并两个有序数组
        vector<int> tmp(right - left + 1);
        int cur1 = left, cur2 = mid + 1, i = 0;
        while(cur1 <= mid && cur2 <= right)
            tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur1++] : nums[cur2++];
        // 4.处理没有遍历完的数组
        while(cur1 <= mid) tmp[i++] = nums[cur1++];
        while(cur2 <= right) tmp[i++] = nums[cur2++];

        // 5.还原
        for (int i = left; i <= right; ++i)
            nums[i] = tmp[i - left];
    }
};

2.全局临时数组

class Solution {
public:
    vector<int> tmp;
    vector<int> sortArray(vector<int>& nums) {
        // 49.专题八_分治-归并_归并排序_C++
        tmp.resize(nums.size());
        mergeSort(nums, 0, nums.size() - 1);
        return nums;
    }
    void mergeSort(vector<int>& nums, int left, int right)
    {
        if (left >= right) return;

        // 1.选择中间点划分区间
        int mid = (right - left) / 2 + left;
        // [left, mid] [mid + 1, right]

        // 2.把左右区间排序
        mergeSort(nums, left, mid);
        mergeSort(nums, mid + 1, right);

        // 3.合并两个有序数组
        int cur1 = left, cur2 = mid + 1, i = 0;
        while(cur1 <= mid && cur2 <= right)
            tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur1++] : nums[cur2++];
        // 4.处理没有遍历完的数组
        while(cur1 <= mid) tmp[i++] = nums[cur1++];
        while(cur2 <= right) tmp[i++] = nums[cur2++];

        // 5.还原
        for (int i = left; i <= right; ++i)
            nums[i] = tmp[i - left];
    }
};

 

LCR 170. 交易逆序对的总数(数组中的逆序对)

LCR 170. 交易逆序对的总数

 

解析

题解

1.升序

class Solution {
public:
    int tmp[50001] = {0};
    int reversePairs(vector<int>& record) {
        // 50.专题八_分治-归并_数组中的逆序对_C++
        // 1.升序
        return mergeSort(record, 0, record.size() - 1);
    }
    int mergeSort(vector<int>& record, int left, int right)
    {
        if (left >= right) return 0;

        int ret = 0;
        // 1. 找中间点, 将数组分成两部分
        int mid =  (right - left) / 2 + left;
        // [left, mid] [mid + 1, right]

        // 2. 左边的个数 + 排序 + 右边的个数 + 排序
        ret += mergeSort(record, left, mid);
        ret += mergeSort(record, mid + 1, right);

        // 3. 一左一右的个数
        int cur1 = left, cur2 = mid + 1, i = 0;
        while(cur1 <= mid && cur2 <= right)
        {
            if (record[cur1] <= record[cur2])
            {
                tmp[i++] = record[cur1++];
            }
            else 
            {
                ret += mid - cur1 + 1;
                tmp[i++] = record[cur2++];
            }
        }
        // 4.处理一下排序
        while(cur1 <= mid) tmp[i++] = record[cur1++];
        while(cur2 <= right) tmp[i++] = record[cur2++];
        for(int j = left; j <= right; ++j)
            record[j] = tmp[j - left];
        return ret;
    }
};

2.降序

class Solution {
public:
    int tmp[50001] = {0};
    int reversePairs(vector<int>& record) {
        // 50.专题八_分治-归并_数组中的逆序对_C++
        // 1.降序
        return mergeSort(record, 0, record.size() - 1);
    }
    int mergeSort(vector<int>& record, int left, int right)
    {
        if (left >= right) return 0;

        int ret = 0;
        // 1. 找中间点, 将数组分成两部分
        int mid =  (right - left) / 2 + left;
        // [left, mid] [mid + 1, right]

        // 2. 左边的个数 + 排序 + 右边的个数 + 排序
        ret += mergeSort(record, left, mid);
        ret += mergeSort(record, mid + 1, right);

        // 3. 一左一右的个数
        int cur1 = left, cur2 = mid + 1, i = 0;
        while(cur1 <= mid && cur2 <= right)
        {
            if (record[cur1] <= record[cur2])
            {
                tmp[i++] = record[cur2++];
            }
            else 
            {
                ret += right - cur2 + 1;
                tmp[i++] = record[cur1++];
            }
        }
        // 4.处理一下排序
        while(cur1 <= mid) tmp[i++] = record[cur1++];
        while(cur2 <= right) tmp[i++] = record[cur2++];
        for(int j = left; j <= right; ++j)
            record[j] = tmp[j - left];
        return ret;
    }
};

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

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

解析

题解

class Solution {
public:
    vector<int> ret;
    vector<int> index; // 记录 nums 中当前元素的原始下标
    int tmpNums[500010];
    int tmpIndex[500010];
    vector<int> countSmaller(vector<int>& nums) {
        // 51.专题八_分治-归并_计算右侧小于当前元素的个数_C++
        int n = nums.size();
        ret.resize(n);
        index.resize(n);

        // 初始一下 index 数组
        for (int i = 0; i < n; ++i)
            index[i] = i;
        mergeSort(nums, 0, n - 1);
        return ret;
    }

    void mergeSort(vector<int>& nums, int left, int right)
    {
        if (left >= right) return;

        // 1.根据中间元素,划分区间
        int mid = (right - left) / 2 + left;
        // [left, mid] [mid + 1, right]

        // 2.先处理左右两部分
        mergeSort(nums, left, mid);
        mergeSort(nums, mid + 1, right);

        // 3.处理一左一右的情况
        int cur1 = left, cur2 = mid + 1, i = 0;
        while(cur1 <= mid && cur2 <= right)
        {
            if (nums[cur1] <= nums[cur2])
            {
                tmpNums[i] = nums[cur2];
                tmpIndex[i++] = index[cur2++];
            }
            else
            {
                ret[index[cur1]] += right - cur2 + 1; // 重点
                tmpNums[i] = nums[cur1];
                tmpIndex[i++] = index[cur1++];
            }
        }

        // 4.处理剩下的排序过程
        while(cur1 <= mid)
        {
            tmpNums[i] = nums[cur1];
            tmpIndex[i++] = index[cur1++];
        }
        while(cur2 <= right)
        {
            tmpNums[i] = nums[cur2];
            tmpIndex[i++] = index[cur2++];
        }
        for (int j = left; j <= right; ++j)
        {
            nums[j] = tmpNums[j - left];
            index[j] = tmpIndex[j - left];
        }
    }
};

493. 翻转对

493. 翻转对

解析

题解

1.降序

class Solution {
public:
    int tmp[50010];
    int reversePairs(vector<int>& nums) {
        // 52.专题八_分治-归并_翻转对_C++
        // 1.降序
        return mergeSort(nums, 0, nums.size() - 1);
    }

    int mergeSort(vector<int>& nums, int left, int right)
    {
        if (left >= right) return 0;

        int ret = 0;
        // 1.先根据中间元素划分区间
        int mid = (right - left) / 2 + left;
        // [left, mid] [mid + 1, right]

        // 2.先计算左右两侧的翻转对
        ret += mergeSort(nums, left, mid);
        ret += mergeSort(nums, mid + 1, right);

        // 3.先计算翻转对的数量
        int cur1 = left, cur2 = mid + 1, i = 0;
        while(cur1 <= mid)
        {
            while(cur2 <= right && nums[cur2] >= nums[cur1] / 2.0) cur2++;
            if (cur2 > right) break;
            ret += right - cur2 + 1;
            cur1++;
        }

        // 4.合并两个有序数组
        cur1 = left, cur2 = mid + 1;
        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 j = left; j <= right; ++j)
            nums[j] = tmp[j - left];
        return ret;
    }
};

2.升序

class Solution {
public:
    int tmp[50010];
    int reversePairs(vector<int>& nums) {
        // 52.专题八_分治-归并_翻转对_C++
        // 1.升序
        return mergeSort(nums, 0, nums.size() - 1);
    }

    int mergeSort(vector<int>& nums, int left, int right)
    {
        if (left >= right) return 0;

        int ret = 0;
        // 1.先根据中间元素划分区间
        int mid = (right - left) / 2 + left;
        // [left, mid] [mid + 1, right]

        // 2.先计算左右两侧的翻转对
        ret += mergeSort(nums, left, mid);
        ret += mergeSort(nums, mid + 1, right);

        // 3.先计算翻转对的数量
        int cur1 = left, cur2 = mid + 1, i = 0;
        while(cur2 <= right)
        {
            while(cur1 <= mid && nums[cur2] >= nums[cur1] / 2.0) cur1++;
            if (cur1 > mid) break;
            ret += mid - cur1 + 1;
            cur2++;
        }

        // 4.合并两个有序数组
        cur1 = left, cur2 = mid + 1;
        while(cur1 <= mid && cur2 <= right)
            tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur1++] : nums[cur2++];
        while(cur1 <= mid) tmp[i++] = nums[cur1++];
        while(cur2 <= right) tmp[i++] = nums[cur2++];

        for (int j = left; j <= right; ++j)
            nums[j] = tmp[j - left];
        return ret;
    }
};

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

清风玉骨

爱了!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值