C++算法 —— 分治(2)归并

文章目录

  • 1、排序数组
  • 2、数组中的逆序对
  • 3、计算右侧小于当前元素的个数
  • 4、翻转对


本篇前提条件是已学会归并排序

1、排序数组

排序数组

在这里插入图片描述

排序数组也可以用归并排序来做。

    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) / 2;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];}}

2、数组中的逆序对

剑指 Offer 51. 数组中的逆序对

在这里插入图片描述

如果暴力枚举,一定是可以解决问题的,但肯定不用这个解法。选择逆序对,可以先把数组分成两部分,左半部分 + 右半部分的逆序对,以及再找左半部分的数字和右半部分数字成对的数,比如上面例子中,7和6,7和4就是这种情况。左 + 右 + 一左一右就是整体的逆序对数量。当这两半部分都处理完后,就扩大区间,继续上述操作。这个解法也就是利用归并排序,归并排序的思路就是划分到最小的区间,只有1个数,它一定是有序的,回到上一层,也就是2个数的区间,让它们排好序,在它右边的也是2个数的区间,重复和它一样的操作,这样两个区间都有序后,再往上走一层,来到4个数的区间,4个数,每一半都是有序的,将整体的4个数排成有序的,再往上走,来到8个数的区间,重复操作。

利用归并排序的思路,我们在两个区间都排成升序后,定义两个指针cur指向两个区间的开头,然后一左一右比较大小,如果cur1比cur2大,那么cur1之后都比cur2大,就可以一次性加上多个逆序对的个数。

下面的代码可以从最小的区间开始一个个代入来理解。从只有2个数的区间开始,走到递归处,分成2个只有1个数的区间,那就会返回0,两处递归走完,来到下面的循环,此时left是0,right是1,mid是0,带入进去会发现,最后的ret只会是0或者1,并且这2个数也在最后排好序了,返回后,来到上一层,也就是走左边递归的那行代码,然后再走右边,也是2个数,也是同样的操作,2个区间排好序了,4个数的区间就一左一右比较大小,找出逆序对,排好序,再走到上一层,8个数的区间也是如此。

class Solution {int tmp[50010];
public: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;//1. 找中间点,将数组分成两部分int mid = (left + right) >> 1;// [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 && cur2 <= right)//while体内原本是归并排序的代码,现在就多加一点{if(nums[cur1] <= nums[cur2]) tmp[i++] = nums[cur1++];else{ret += mid - cur1 + 1;tmp[i++] = nums[cur2++];}}//4. 处理排序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;}
};

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

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

在这里插入图片描述

此题和上一个题有相同之处,也是分治,也是利用归并排序,这道题可以看作,当前元素后面,有多少比我小的,而上一题则是当前元素前面,有多少比我大的。仔细想一想,上一题是排升序,这一题排降序则更为合适。这题和上一题还有不同的地方。

cur1和cur2,排成降序,如果cur1 <= cur2,cur2++,因为我们要找比当前元素小的;如果cur1 > cur2,由于是降序,那么cur2之后的肯定都小,但这里不能加上ret,我们要返回一个数组,要把这个数加在当前元素的原始下标,因为数组已经被我们给排序了,所以要找原始下标。这里的做法就是从最一开始就除了tmp外,再定义一个数组,存储着原始下标,因为这时候还没开始排序,可以找得到,然后每次原数组元素变换位置,这个下标数组也跟着变换。

我们要定义四个数组,一个是结果数组,一个是原始下标数组,一个是辅助数组,也就是tmp,记录改动过的顺序,一个是下标辅助数组,记录改动后的下标顺序。在while循环中,每次更新tmp,下标辅助数组也跟着更新。如果cur1大于cur2,那么除了更新,还需要往结果数组中写入个数,要在当前元素的原始下标处写入个数,这里最好要画图来理解,画原始下标和下标变动后的图。在最后for循环中的排序,除了原数组nums,还有原始下标数组也要排序。

    vector<int> index;//原始元素下标vector<int> res;//最终结果int tmp[500010];//排序辅助数组int tmpIndex[500010];//元素下标的辅助数组
public:vector<int> countSmaller(vector<int>& nums) {int sz = nums.size();index.resize(sz);res.resize(sz);for(int i = 0; i < sz; i++){index[i] = i;}mergeSort(nums, 0, sz - 1);return res;}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){if(nums[cur1] <= nums[cur2]){tmp[i] = nums[cur2];tmpIndex[i++] = index[cur2++];}else {res[index[cur1]] += right - cur2 + 1;//经历了之前的排序,index已经记录下了最新的下标变动,这里就可以直接用cur1来获取正确的下标tmp[i] = nums[cur1];tmpIndex[i++] = index[cur1++];}}while(cur1 <= mid){tmp[i] = nums[cur1];tmpIndex[i++] = index[cur1++];}while(cur2 <= right){tmp[i] = nums[cur2];tmpIndex[i++] = index[cur2++];}for(int j = left; j <= right; j++){nums[j] = tmp[j - left];index[j] = tmpIndex[j - left];}}

4、翻转对

翻转对

在这里插入图片描述

还是一样的思路。左半部分,右半部分,然后一左一右。不过这里的条件不一样。这里的解决办法有两个,一个是计算当前元素后面有多少元素的两倍比我小,另一个是计算当前元素之前,有多少元素的一半比我大,这两个的高效顺序分别是降序和升序。

第一个思路,cur1和cur2,找当前元素的后面,那就以cur1为重点,如果cur2的2倍大于等于cur1,cur2就往后走,如果小于,那么后面的肯定都小于。第二个思路,cur1和cur2,找当前元素的前面,那就以cur2为重点,如果cur1的一半比cur2小,那么cur1后的肯定都符合条件,如果cur1的一半大于cur2,那cur1往后走。最后合并两个有序数组。

    int tmp[50010];
public: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) >> 1;ret += mergeSort(nums, left, mid);ret += mergeSort(nums, mid + 1, right); int cur1 = left, cur2 = mid + 1, i = left;//先计算翻转对,0还是left都行/*while(cur1 <= mid)//这里排降序,也可以排升序{while(cur2 <= right && nums[cur2] >= nums[cur1] / 2.0) cur2++;//2.0是为了防止除不尽if(cur2 > right) break;ret += right - cur2 + 1;cur1++;}*/while(cur2 <= right)//升序{while(cur1 <= mid && nums[cur2] >= nums[cur1] / 2.0) cur1++;if(cur1 > mid) 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++];//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;}

结束。

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

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

相关文章

【C++】—— c++11之智能指针

前言&#xff1a; 本期&#xff0c;我们将要学习的是在c11中新提出的概念——异常指针&#xff01; 目录 &#xff08;一&#xff09;智能指针的引入 &#xff08;二&#xff09;内存泄漏 1、什么是内存泄漏&#xff0c;内存泄漏的危害 2、内存泄漏分类&#xff08;了解&a…

Stable Diffusion 从入门到企业级应用010

一、前言 本文是《Stable Diffusion 从入门到企业级应用实战》系列的第四部分能力进阶篇《Stable Diffusion ControlNet v1.1 图像精准控制》的第010篇 利用Stable Diffusion ControlNet 法线贴图模型精准控制图像生成。本部分内容&#xff0c;位于整个Stable Diffusion生态体…

2023年王炸面试题每日一练--为什么会有精度的损失

基本数据类型中为什么会出现精度损失&#xff0c;怎么样会避免出现精度损失 loat 32位 出现精度损失的原因&#xff1a; 输入的值为十进制&#xff0c;而在计算的过程中&#xff0c;是要把十进制的小数位值在有限位的情况下转变为二进制的小数&#xff0c;就会出现精度的损失…

VScode远程连接主机

一、前期准备 1、Windows安装VSCode&#xff1b; 2、在VSCode中安装PHP Debug插件&#xff1b; 3、安装好Docker 4、在容器中安装Xdebug ①写一个展现phpinfo的php文件 <?php phpinfo(); ?>②在浏览器上打开该文件 ③复制所有信息丢到Xdebug: Installation instr…

了解 HarmonyOS

引言 在开始 HarmonyOS 开发之前&#xff0c;了解其背景、特点和架构是非常重要的。本章将为你提供一个全面的 HarmonyOS 概览。 目录 什么是 HarmonyOS HarmonyOS 的发展历程 HarmonyOS 的特点 HarmonyOS 的架构 HarmonyOS 与其他操作系统的比较 1. 什么是 HarmonyOS …

学习笔记230810--vue项目中get请求的两种传参方式

问题描述 今天写了一个对象方式传参的get请求接口方法&#xff0c;发现没有载荷&#xff0c;ip地址也没有带查询字符串&#xff0c;数据也没有响应。 代码展示 错误分析 实际上这里的query是对象方式带参跳转的参数名&#xff0c;而get方法对象方式传参的参数名是parmas 解…

Nmap 7.94 发布:新功能!

Nmap 的最新版本 7.94 在其 26 岁生日之际发布。 最重要的升级是在所有平台上将 Zenmap 和 Ndiff 从 Python 2 迁移到 Python 3。 这个新版本的 Nmap 7.94 进行了升级&#xff0c;进行了多项改进&#xff0c;修复了一些关键错误&#xff0c;并添加了新的 Npcap、操作系统指纹…

ubuntu入门01——windows上直接部署linux(WSL)

win10安装参考如下教程&#xff1a; 旧版 WSL 的手动安装步骤 | Microsoft Learn 说明&#xff1a;该文档是我按如上教程安装使用Ubuntu写的回顾&#xff0c;家人们参考官方教程更妙。 1.启用适用于Linux的wundows子系统 2.启用虚拟机功能 dism.exe /online /enable-feat…

Java“魂牵”京东店铺所有商品数据接口,京东店铺所有商品API接口,京东API接口申请指南

要通过京东的API获取店铺所有商品数据&#xff0c;您可以使用京东开放平台提供的接口来实现。以下是一种使用Java编程语言实现的示例&#xff0c;展示如何通过京东开放平台API获取整店商品数据&#xff1a; 首先&#xff0c;确保您已注册成为京东开放平台的开发者&#xff0c;…

已解决‘jupyter‘ 不是内部或外部命令,也不是可运行的程序或批处理文件报错

本文摘要&#xff1a;本文已解决‘jupyter‘ 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件的相关报错问题&#xff0c;并系统性地总结提出了几种可用解决方案。同时结合人工智能GPT排除可能得隐患及错误。 &#x1f60e; 作者介绍&#xff1a;我是程序员洲洲…

单元测试及其工具Junit

1.单元测试是什么 单元测试是开发者编写的一小段代码&#xff0c;用于检验被测代码的一个很小的、很明确的功能是否正确&#xff0c;通常而言&#xff0c;一个单元测试是用于判断某个特定条件&#xff08;或者场景&#xff09;下某个特定函数的行为。 单元测试是软件测试的一种…

Redis 缓存穿透、击穿、雪崩

一、缓存穿透 1、含义 缓存穿透是指查询一个缓存中和数据库中都不存在的数据&#xff0c;导致每次查询这条数据都会透过缓存&#xff0c;直接查库&#xff0c;最后返回空。 2、解决方案 1&#xff09;缓存空对象 就是当数据库中查不到数据的时候&#xff0c;我缓存一个空对象…

【LLM】chatglm-6B模型训练和推理

本篇文章记录下 chatglm-6B 训练和推理过程 环境&#xff1a;Ubuntu 20.04 1.13.0cu116 chatglm-6B 源代码仓库&#xff1a;链接 chatglm-6B 模型权重&#xff1a;链接 源代码及模型 clone 到本地 这里使用的是 THUDM 在 hugging face 开源的模型。 因为模型比较大&#xff…

Linux c++开发-03-使用CMake组织工程

一、简单文件的编译 有如下的目录结构&#xff1a; 其中 helloworld.cpp如下&#xff1a; #include <iostream> using namespace std; int main() {printf("hello world my name is Ty!");return 0; }CMakeLists.txt如下&#xff1a; cmake_minimum_requir…

20. python从入门到精通——Flask框架

目录 安装虚拟环境和Flask 第一个Flask程序 Flask的调试模式 路由 变量规则&#xff1a;当在页面中输出变量的时候就需要遵循变量的规则 构造URL 在route函数中设置http方法 获取静态文件路径 蓝图 模板 Web表单 CSRF 安装虚拟环境和Flask Flask框架主要依赖两个库…

小白学Go 基础02-了解Go语言的诞生与演进

Go语言诞生于何时&#xff1f;它的最初设计者是谁&#xff1f;它为什么被命名为Go&#xff1f;它的设计目标是什么&#xff1f;它如今发展得怎么样&#xff1f;带着这些问题&#xff0c;我们一起穿越时空&#xff0c;回到2007年9月Go语言诞生的那一历史时刻吧。 Go语言的诞生 …

C# 如何将使用的Dll嵌入到.exe应用程序中?

文章目录 前言详细实操简要步骤 前言 有没有想自己开发的exe保留一点神秘&#xff0c;不想让他人知道软件使用了哪些dll; 又或许是客户觉得一个软件里面的dll文件太多了&#xff0c;能不能简单一点&#xff0c;直接双击.exe就可以直接运行了&#xff0c;别搞那么多乱七八糟的。…

【Datawhale】AI夏令营第三期——基于论文摘要的文本分类笔记(上)

暑期参加了Datawhale的第三期AI夏令营&#xff0c;学习的是NLP方向&#xff0c;在此期间&#xff0c;我们通过比赛打榜的形式进行NLP的学习。今天&#xff0c;主要分享和记录一下这一期夏令营的学习历程和笔记。 文章目录 赛题背景赛题任务赛题数据集评价指标解题思路任务一&am…

linux中学习控制进程的要点

1. 进程创建 1.1 fork函数 #include <unistd.h> pid_t fork(void); 返回值&#xff1a;自进程中返回0&#xff0c;父进程返回子进程id&#xff0c;出错返回-1 进程调用fork&#xff0c;当控制转移到内核中的fork代码后&#xff0c;内核会做以下操作 分配新的内存块和…

Java“牵手”京东商品列表数据,关键词搜索京东商品数据接口,京东API申请指南

京东商城是一个网上购物平台&#xff0c;售卖各类商品&#xff0c;包括服装、鞋类、家居用品、美妆产品、电子产品等。要获取京东商品列表和商品详情页面数据&#xff0c;您可以通过开放平台的接口或者直接访问京东商城的网页来获取商品详情信息。以下是两种常用方法的介绍&…