C++ 归并排序OJ

news2024/11/26 18:36:45

目录

1、912. 排序数组

2、LCR 170. 交易逆序对的总数

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

4、493. 翻转对


1、912. 排序数组

 

思路:本次使用归并排序 ,快速排序相当于二叉树的前序遍历,而归并排序相当于后序遍历。

  • 归并排序是一种有效的排序算法,采用分治策略,将问题分解成一系列更小的问题,然后解决这些小问题,并将解决方案合并以产生原始问题的解决方案。下面是对这个过程的逐步解释。
  • 归并排序的工作原理

    归并排序通过递归地将数组分成越来越小的部分,直到每个部分只有一个元素(自然是有序的),然后将这些有序的部分合并成较大的有序部分,直至整个数组排序完成。这个过程中,合并操作是通过比较来自两个不同部分的元素并选择较小的那个来实现的,这保证了合并后的数组也是有序的。

  • 性能

    归并排序的时间复杂度为O(n log n),其中n是数组的长度。这是因为每一层递归操作都需要遍历整个数组(O(n)),而递归树的深度为log n。归并排序需要额外的存储空间来存储临时数组,因此它的空间复杂度为O(n)。

class Solution {
public:
    vector<int> tmp;
    vector<int> sortArray(vector<int>& nums) {
        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;
        int mid = left + ((right - left) >> 1);
        mergeSort(nums, left, mid);
        mergeSort(nums, mid + 1, right);
        int cur1 = left, cur2 = mid + 1, i = 0;
        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 i = left; i <= right; i++)
            nums[i] = tmp[i - left];
    }
};
  • vector<int> tmp;:一个临时向量,用于归并排序过程中的合并操作。
  • vector<int> sortArray(vector<int>& nums)

    • 接受一个整数向量nums作为输入,并返回排序后的向量。
    • tmp.resize(nums.size());:首先,将临时向量tmp的大小调整为与输入向量nums相同,确保有足够的空间进行合并操作。
    • mergeSort(nums, 0, nums.size() - 1);:调用mergeSort方法对整个数组进行排序,传入的参数是数组的左右边界索引。
  • void mergeSort(vector<int>& nums, int left, int right)

    • 这是一个递归方法,用于对数组nums从索引leftright进行排序。
    • 如果left大于等于right,表示区间内最多只有一个元素,不需要排序,直接返回。
    • 计算中点mid,将数组分为两部分:leftmidmid + 1right
    • 递归调用mergeSort分别对这两部分进行排序。
    • 使用三个指针cur1cur2i,分别指向左半部分的起始位置、右半部分的起始位置和临时数组tmp的起始位置。
    • 通过比较cur1cur2所指向的元素,选择较小的元素放入tmp中,并移动相应的指针和i
    • 如果左半部分或右半部分有剩余元素,将它们复制到tmp中。
    • 将临时数组tmp中的元素复制回原数组nums的相应位置。

2、LCR 170. 交易逆序对的总数

 思路:采用分支策略,使用归并排序处理。利用归并排序先在左区间统计逆序对个数,再统计右区间,左右区间统计完回到上层,左右区间作为左区间继续递归统计,递归排序既可以排升序也可以排降序,都可以解决本题。

升序: 找出该数之前有多少个比它大的

class Solution {
public:
    int tmp[50010];
    int reversePairs(vector<int>& record) {
        return mergeSort(record, 0, record.size() - 1);
    }
    int mergeSort(vector<int>& record, int left, int right) {
        if (left >= right)
            return 0;
        int mid = left + ((right - left) >> 1);
        int ret = 0;
        ret += mergeSort(record, left, mid);
        ret += mergeSort(record, mid + 1, right);
        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++];
            }
        }
        while (cur1 <= mid) {
            tmp[i++] = record[cur1++];
        }
        while (cur2 <= right) {
            tmp[i++] = record[cur2++];
        }
        for (int i = left; i <= right; i++) {
            record[i] = tmp[i - left];
        }
        return ret;
    }
};

降序:找出该数之前有多少个比它小的

class Solution {
public:
    int tmp[50010];
    int reversePairs(vector<int>& record) {
        return mergeSort(record, 0, record.size() - 1);
    }
    int mergeSort(vector<int>& record, int left, int right) {
        if (left >= right)
            return 0;
        int mid = left + ((right - left) >> 1);
        int ret = 0;
        ret += mergeSort(record, left, mid);
        ret += mergeSort(record, mid + 1, right);
        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++];
            }
        }
        while (cur1 <= mid) {
            tmp[i++] = record[cur1++];
        }
        while (cur2 <= right) {
            tmp[i++] = record[cur2++];
        }
        for (int i = left; i <= right; i++) {
            record[i] = tmp[i - left];
        }
        return ret;
    }
};

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

思路:采用分支策略,使用归并排序处理。但是在我们归并排序的过程中,元素的下标是会跟着变化的,因此我们需要⼀个辅助数组,来将数组元素和对应的下标绑定在⼀起归并,也就是再归并元素的时候,顺势将下标也转移到对应的位置上。

class Solution {
public:
    vector<int> index;
    vector<int> ret;
    int tmpNums[500000];
    int tmpIndex[500000];

    vector<int> countSmaller(vector<int>& nums) {
        int n = nums.size();
        ret.resize(n);
        index.resize(n);
        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;

        int mid = left + ((right - left) >> 1);
        mergeSort(nums, left, mid);
        mergeSort(nums, mid + 1, right);
        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++];
            }
        }
        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];
        }
    }
};

类成员解释

  • vector<int> index;:用于存储每个元素在原数组中的索引。
  • vector<int> ret;:用于存储最终的结果,即每个元素右侧小于它的元素数量。
  • int tmpNums[500000];:临时数组,用于归并排序过程中存储中间结果。
  • int tmpIndex[500000];:临时数组,用于存储归并排序过程中元素的索引。

方法解释

  • vector<int> countSmaller(vector<int>& nums):这是主方法,用于初始化并开始归并排序过程。

    • 初始化retindex数组。
    • 调用mergeSort方法进行归并排序。
  • void mergeSort(vector<int>& nums, int left, int right):这是归并排序的实现方法。

    • 如果left大于等于right,则不需要排序,直接返回。
    • 计算中点mid,然后递归地对左半部分和右半部分进行归并排序。
    • 接下来是归并过程,其中比较关键的是在比较左右两部分的元素时,如果左边的元素大于右边的元素,那么意味着左边的这个元素大于右边所有未被合并的元素,因此ret[index[cur1]]需要加上右边剩余元素的数量(right - cur2 + 1)。
    • 最后,将排序好的部分复制回原数组nums和索引数组index

4、493. 翻转对

思路:采用分支策略,使用归并排序处理。

降序—找元素后面小的 

class Solution {
public:
    int tmp[50001];
    int reversePairs(vector<int>& nums) {
        return mergeSort(nums, 0, nums.size() - 1);
    }
    int mergeSort(vector<int>& nums, int left, int right) {
        if (left >= right)
            return 0;
        int ret = 0;
        int mid = left + ((right - left) >> 1);
        ret += mergeSort(nums, left, mid);
        ret += mergeSort(nums, mid + 1, right);
        int cur1 = left, cur2 = mid + 1, i = left;
        while (cur1 <= mid) {
            while (cur2 <= right && nums[cur2] >= nums[cur1] / 2.0) {
                cur2++;
            }
            if (cur2 > right)
                break;
            ret += right - cur2 + 1;
            cur1++;
        }
        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];
        }
        return ret;
    }
};
  • int reversePairs(vector<int>& nums):这是主方法,用于开始归并排序过程,并返回重要翻转对的数量。
  • int mergeSort(vector<int>& nums, int left, int right):这是归并排序的实现方法,同时也是计算重要翻转对的核心方法。
    • 如果left大于等于right,则不需要处理,直接返回0。
    • 通过递归调用mergeSort方法,分别对数组的左半部分和右半部分进行排序,并计算左半部分和右半部分内部各自的重要翻转对数量,累加到ret
    • 在合并两个已排序的部分之前,先计算跨越左右部分的重要翻转对数量。这是通过两个指针cur1cur2实现的,cur1遍历左半部分,cur2遍历右半部分,当nums[cur2] >= nums[cur1] / 2.0时,cur2向右移动,直到找到第一个使得nums[i] > 2*nums[j]j,此时right - cur2 + 1即为对于当前cur1指向的元素,满足条件的翻转对数量。
    • 然后,进行正常的归并排序合并操作,将两个部分合并为一个有序数组。
    • 最后,将临时数组tmp中的排序结果复制回原数组nums

升序—找元素前面大的

class Solution {
public:
    int tmp[50001];
    int reversePairs(vector<int>& nums) {
        return mergeSort(nums, 0, nums.size() - 1);
    }
    int mergeSort(vector<int>& nums, int left, int right) {
        if (left >= right)
            return 0;
        int ret = 0;
        int mid = left + ((right - left) >> 1);
        ret += mergeSort(nums, left, mid);
        ret += mergeSort(nums, mid + 1, right);
        int cur1 = left, cur2 = mid + 1, i = left;
        while (cur2 <= right) {
            while (cur1 <= mid && nums[cur1] / 2.0 <= nums[cur2]) {
                cur1++;
            }
            if (cur1 > right)
                break;
            ret += mid - cur1 + 1;
            cur2++;
        }
        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];
        }
        return ret;
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1492936.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Java设计模式】三、简单工厂、工厂方法模式、抽象工厂模式

文章目录 0、案例&#xff1a;咖啡屋1、简单工厂模式 静态工厂&#xff08;不属于23种之列&#xff09;2、工厂方法模式3、抽象工厂模式4、简单工厂模式 配置文件解除耦合5、JDK源码中对工厂模式的应用 0、案例&#xff1a;咖啡屋 模拟咖啡店点餐。咖啡有多种&#xff0c;抽…

vue3+element plus 实现百度地图显示路径

添加依赖 <!-- index.html --><script type"text/javascript" src"//api.map.baidu.com/getscript?v3.0&akyI6kBeC9G4LntEWXklE2iNHwRUrmFEQc"></script><script type"text/javascript" src"//api.map.baidu.co…

【vue/组件封装】封装一个带条件筛选的搜索框组件(多组条件思路、可多选)详细流程

引入&#xff1a;实现一个带有筛选功能的搜索框&#xff0c;封装成组件&#xff1b; 搜索框长这样子&#xff1a; 点击右侧筛选图标后弹出层&#xff0c;长这样子&#xff1a; 实际应用中有多组筛选条件&#xff0c;这里为了举栗子就展示一组&#xff1b; 预览&#xff1a;…

【小白学机器学习7】相关系数R,决定系数R2和SST=SSR+SSE, 离差,偏差,方差,标准差,变异系数,标准误。

目录 1 各种数据指标&#xff0c;分类整理 1.0 关于数据/值有3种 1.1 第1类&#xff1a;描述一堆数据特征的指标&#xff1a;集中度&#xff0c;离散度&#xff0c;形状特征 1.2 第2类&#xff1a;判断预测y值和观测值差距的指标 1.3 第3类&#xff1a;描述误差的各种指标…

给定长度为n的数组a,每一次操作可以使相邻两个元素都+1或者-1,可以进行任意次操作,求最终能否使数组非递减

题目 思路&#xff1a; #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 1e6 5, inf 1e18, maxm 4e4 5, …

矩阵爆破逆向-条件断点的妙用

不知道你是否使用过IDA的条件断点呢&#xff1f;在IDA进阶使用中&#xff0c;它的很多功能都有大作用&#xff0c;比如&#xff1a;ida-trace来跟踪调用流程。同时IDA的断点功能也十分强大&#xff0c;配合IDA-python的输出语句能够大杀特杀&#xff01; 那么本文就介绍一下这个…

K线实战分析系列之十八:十字线——判断行情顶部的有效信号

K线实战分析系列之十八&#xff1a;十字线——判断行情顶部的有效信号 一、十字线二、十字线总结三、三种特殊十字线四、长腿十字线五、墓碑十字线六、蜻蜓十字线七、特殊十字线总结 一、十字线 重要的反转信号 幅度较大的下跌&#xff0c;出现一根十字线&#xff0c;正好是在…

配置化脚手架cli工具开发实践

背景 我们服务于政务行业&#xff0c;正在打造一个集代码开发、数据集成、应用管理、一体化运维监控的应用支撑平台。 以此为导向&#xff0c;作为开发的第一步&#xff0c;代码工程创建应当为后续的集成、管理及监控等服务。所以区别于一般的cli工具&#xff0c;我们要做的工…

x6.js 流程图绘制笔记,常用函数

官方参考网站如下&#xff1a;https://antv-x6.gitee.io/zh/docs/tutorial/about 安装x6 输入以下命令 npm install antv/x6 --save 引用插件代码如下&#xff1a; import { Graph } from antv/x6; 创建绘制区域 this.guiX6 new Graph({container: document.querySelect…

相机恢复,这几个方法很重要!

“我的相机用了才不到一年&#xff0c;现在不知道是什么原因&#xff0c;有一些拍摄的图片找不到了&#xff0c;有什么方法可以恢复丢失的照片吗&#xff1f;” 对于热爱记录生活的用户来说&#xff0c;相机出现问题或相机数据丢失&#xff0c;都是一件很让人难过的事情。 在使…

文件上传{session文件包含以及条件竞争、图片文件渲染绕过(gif、png、jpg)}

session文件包含以及条件竞争 条件&#xff1a; 知道session文件存储在哪里 一般的默认位置&#xff1a; /var/lib/php/sess_PHPSESSID /var/lib/php/sessions/sess_PHPSESSID /tmp/sess_PHPSESSID /tmp/sessions/sess_PHPSESSID ####在没做过设置的情况下一般都是存储在/var…

基于Harris角点的室内三维全景图拼接算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1Harris角点检测原理 4.2 Harris响应函数 4.3 角点检测与筛选 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 dirs datasheet/;% 定义…

(十三)上市企业实施IPD成功案例分享之——杜邦

在化工行业&#xff0c;说起杜邦公司&#xff0c;可谓是“顶流”企业。作为一家有着200多年历史&#xff0c;历经了三个世纪的化工巨头&#xff0c;杜邦企业的发展史&#xff0c;就是化学工业&#xff0c;乃至整个科技水平的进步史。从1802年杜邦创立时主营的火药&#xff0c;到…

Redis 缓存机制如何提高应用程序的性能?

在数字时代&#xff0c;一拍脑门儿我们就能感觉到信息的海量和处理速度的迫切。不管是刷个微博、下个单&#xff0c;还是玩个游戏&#xff0c;我们都希望能快上加快&#xff0c;一点不拖泥带水。这时候&#xff0c;缓存技术就扮演了个大英雄的角色&#xff0c;它能让数据存取的…

Windows安装Go语言及VScode配置

最近搞自己的网站时突然想起来很多上学时的事&#xff0c;那会美国总统还是奥巴马&#xff0c;网页课教的是DreamWeaver跟Photoshop&#xff0c;其他语言像PHP、Java8、Python都有学一点&#xff0c;讲究一个所见即所得。虽然是信管专业那时和斌桑班长对新语言很感兴趣&#xf…

LC打怪录 希尔排序Shell sort 912.排序数组

Theory 希尔排序本质上是对插入排序的一种优化&#xff0c;它利用了插入排序的简单&#xff0c;又克服了插入排序每次只交换相邻两个元素的缺点。它的基本思想是&#xff1a; 将待排序数组按照一定的间隔分为多个子数组&#xff0c;每组分别进行插入排序。这里按照间隔分组指…

第二证券|中证1000认沽期权是什么?怎么买?

中证1000指数期权是以中证1000指数为标的资产的衍生品&#xff0c;其间中证1000认沽期权是指期权买方有权在约好的时刻以约好的价格将必定数量的标的资产卖给期权卖方的中证1000指数期权合约。 个人投资者想要生意认沽期权&#xff0c;需求去证券公司开通期权账户&#xff0c;…

阿里云2核4G服务器支持多少人同时在线?

2核4G服务器支持多少人在线&#xff1f;阿里云服务器网账号下的2核4G服务器支持20人同时在线访问&#xff0c;然而应用不同、类型不同、程序效率不同实际并发数也不同&#xff0c;2核4G服务器的在线访问人数取决于多个变量因素&#xff1a; 2核4G&#xff1a;2核CPU和4G内存对…

1.2_1 分层结构、协议、接口和服务

1.2_1 分层结构、协议、接口和服务 &#xff08;一&#xff09;为什么要分层&#xff1f; 主机A如果想要向主机B发送文件&#xff0c;则一定要经过中间的一些介质、链路。 发送文件前要完成的工作&#xff1a; 1.发起通信的计算机必须将数据通信的通路进行激活。 所谓的激活&a…

【码银送书第十三期】《ChatGPT原理与架构》

OpenAI 在 2022 年 11 月推出了人工智能聊天应用—ChatGPT。它具有广泛的应用场景&#xff0c;在多项专业和学术基准测试中表现出的智力水平&#xff0c;不仅接近甚至有时超越了人类的平均水平。这使得 ChatGPT 在推出之初就受到广大用户的欢迎&#xff0c;被科技界誉为人工智能…