【算法专题】归并排序

1. 排序数组

912. 排序数组 - 力扣(LeetCode)

今天我们使用归并排序来对数组进行排序,实际上,归并排序和快速排序是有一定相似之处的,都运用了分而治之的思想提升了排序效率。快速排序的实现思路是每次排序把区间划分为小于基准元素、等于基准元素、大于基准元素三个部分,直至数组整体有序为止;而归并排序的实现思路则是每次排序把区间平均划分为两个部分,分别对这两个部分再次排序,然后把这两个部分合并,重复这个过程直至子数组为一。

显然合并数组这个操作是需要一个数组进行辅助的,由于归并排序过程中两个相等的元素在数组中的位置不会发生改变,所以这是一个稳定的排序算法,虽然在不要求稳定的情况下,都是快速排序比归并排序更快,但归并排序也有自己的应用场景,这点我们在后面会提到。

cpp 复制代码
class Solution {
public:
    vector<int> temp;
    vector<int> sortArray(vector<int>& nums) 
    {
        temp.resize(nums.size());
        mergesort(nums, 0, nums.size() - 1);
        return nums;
    }
    void mergesort(vector<int> &nums, int left, int right)
    {
        if(left >= right) return;
        int mid = (left + right) >> 1;
        mergesort(nums, left, mid);
        mergesort(nums, mid + 1, right);
        int cur1= left, cur2 = mid + 1, i = 0;
        while(cur1 <= mid && cur2 <= right)
        {
            temp[i++] = (nums[cur1] <= nums[cur2]) ? nums[cur1++] : nums[cur2++];
        }
        while(cur1 <= mid) temp[i++] = nums[cur1++];
        while(cur2 <= right) temp[i++] = nums[cur2++];
        for(int j = left; j <= right; j++)
        {
            nums[j] = temp[j - left];
        }
    }
};

2. 交易逆序对的总数

LCR 170. 交易逆序对的总数 - 力扣(LeetCode)

依据题意,我们需要求出一个数组中的逆序对总数,逆序对的定义是前面的数大于后面的数时,这两个数可以组成逆序对。首先能想到的肯定是暴力枚举,两层for循环列举出所有符合条件的逆序对情况,但既然这是困难题,暴力枚举法肯定是通过不了的,所以我们要想办法对暴力法做出优化。

首先,如果我们把数组平均分为左右两个部分,那么要查找逆序对的步骤就是在左半部分找逆序对、在右半部分找逆序对、左右部分各取一个数,找逆序对。这样一来,就能找出所有满足条件的逆序对了,这时大家可能就会奇怪了,这不还是相当于枚举吗?确实是这样,但如果我们在找完左半部分逆序对后对左边进行排序、找完右半部分逆序对后对右边进行排序、在找完左右部分的逆序对后对数组整体进行排序,大家可能发现了,这样一来我们就能够用归并排序来对求逆序对的流程进行优化了。

为什么说排序能够优化查找逆序对的效率呢?我举个例子大家就明白了。

大家可以发现,当nums[cur1] > nums[cur2]时,我们就一次性找到了mid-cur1+1个符合条件的逆序对!和暴力枚举法比起来,大大提升了效率!

cpp 复制代码
class Solution {
public:
    vector<int> temp;
    int reversePairs(vector<int>& record) 
    {
        temp.resize(record.size());
        return mergesort(record, 0, record.size() - 1);
    }
    int mergesort(vector<int> &nums, int left, int right)
    {
        if(left >= right) return 0;
        int ret = 0;
        int mid = (left + right) >> 1;
        ret += mergesort(nums, left, mid);
        ret += mergesort(nums, mid + 1, right);
        int cur1 = left, cur2 = mid + 1, i = 0;
        while(cur1 <= mid && cur2 <= right)
        {
            if(nums[cur1] <= nums[cur2])
            {
                temp[i++] = nums[cur1++];
            }
            else
            {
                ret += mid - cur1 + 1;
                temp[i++] = nums[cur2++];
            }
        }
        while(cur1 <= mid) temp[i++] = nums[cur1++];
        while(cur2 <= right) temp[i++] = nums[cur2++];
        for(int j = left; j <= right; j++)
        {
            nums[j] = temp[j - left];
        }
        return ret;
    }
};
相关推荐
君义_noip31 分钟前
信息学奥赛一本通 1524:旅游航道
c++·算法·图论·信息学奥赛
烁34739 分钟前
每日一题(小白)动态规划篇5
算法·动态规划
独好紫罗兰40 分钟前
洛谷题单2-P5717 【深基3.习8】三角形分类-python-流程图重构
开发语言·python·算法
滴答滴答嗒嗒滴1 小时前
Python小练习系列 Vol.8:组合总和(回溯 + 剪枝 + 去重)
python·算法·剪枝
egoist20231 小时前
【C++指南】一文总结C++二叉搜索树
开发语言·数据结构·c++·c++11·二叉搜索树
lidashent1 小时前
数据结构和算法——汉诺塔问题
数据结构·算法
小王努力学编程2 小时前
动态规划学习——背包问题
开发语言·c++·学习·算法·动态规划
f狐0狸x4 小时前
【蓝桥杯每日一题】4.1
c语言·c++·算法·蓝桥杯
ん贤4 小时前
2023第十四届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组(真题&题解)(C++/Java题解)
java·c语言·数据结构·c++·算法·蓝桥杯
梭七y4 小时前
【力扣hot100题】(022)反转链表
算法·leetcode·链表