[Algorithm][分治 - 归并排序][排序数组][交易逆序对的总数][计算右侧小于当前元素的个数][翻转对]详细讲解

目录

  • 0.原理讲解
  • 1.排序数组
    • 1.题目链接
    • 2.代码实现
  • 2.交易逆序对的总数
    • 1.题目链接
    • 2.算法原理详解
    • 3.代码实现
  • 3.计算右侧小于当前元素的个数
    • 1.题目链接
    • 2.算法原理详解
    • 3.代码实现
  • 4.翻转对
    • 1.题目链接
    • 2.算法原理详解
    • 3.代码实现


0.原理讲解

  • 归并排序的流程充分的体现了**「分⽽治之」**的思想,⼤体过程分为两步
    • :将数组⼀分为⼆为两部分,⼀直分解到数组的⻓度为1 ,使整个数组的排序过程被分为「左半部分排序」+「右半部分排序」
    • :将两个较短的**「有序数组合并成⼀个⻓的有序数组」**,⼀直合并到最初的⻓度
    • 本质类似二叉树的后序遍历
      请添加图片描述

1.排序数组

1.题目链接

  • 排序数组

2.代码实现

  • 递归时,创建vector开销挺大的 -> 辅助数组放全局
class Solution 
{vector<int> assist; // 归并时的辅助数组
public:vector<int> sortArray(vector<int>& nums) {assist.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 = left + (right - left) / 2;// 2.排序左右区间// [left, mid] [mid + 1, right]MergeSort(nums, left, mid);MergeSort(nums, mid + 1, right);// 3.合并两个有序数组int cur1 = left, cur2 = mid + 1, i = 0;while(cur1 <= mid && cur2 <= right){assist[i++] = nums[cur1] <= nums[cur2] ? nums[cur1++] : nums[cur2++];}// 4.处理没有遍历完的数组while(cur1 <= mid){assist[i++] = nums[cur1++];}while(cur2 <= right){assist[i++] = nums[cur2++];}// 5.还原for(int i = left; i <= right; i++){nums[i] = assist[i - left];}}
};

2.交易逆序对的总数

1.题目链接

  • 交易逆序对的总数

2.算法原理详解

  • 解法:利用归并排序的分治过程

    • 左半部分 -> 左派序 -> 右半部分 -> 右排序 -> 一左一右 -> 排序
    • 在归并排序的合并过程中统计出逆序对的数量
  • 注意:默认都是升序,如果掌握升序,结合问题,降序的归并过程也是可以解决问题的

  • 为什么可以利用归并排序

    • 如果将数组从中间划分成两个部分,那么可以将逆序对产⽣的⽅式划分成三组
      • 全部从左数组中选择
      • 全部从右数组中选择
      • ⼀个选左数组另⼀个选右数组
    • 根据排列组合的分类相加原理三种情况下产⽣的逆序对的总和,正好等于总的逆序对数量
    • ⽽这个思路正好匹配归并排序的过程
      • 先排序左数组
      • 再排序右数组
      • 左右数组合二为一
    • 因此,可以利⽤归并排序的过程
      • 先求出左半数组中逆序对的数量
      • 再求出右半数组中逆序对的数量
      • 最后求出⼀个选择左边,另⼀个选择右边情况下逆序对的数量,三者相加即可
  • 为什么要这么做?

    • 在归并排序合并的过程中,得到的是两个有序的数组
    • 可以利⽤数组的有序性,快速统计出逆序对的数量,⽽不是将所有情况都枚举出来
  • 如何在合并两个有序数组的过程中,统计出逆序对的数量?

    • 合并两个有序序列时求逆序对的⽅法有两种
      • 快速统计出某个数前⾯有多少个数⽐它⼤ <- 升序
        • 降序无法实现
      • 快速统计出某个数后⾯有多少个数⽐它⼩ <- 降序
        • 升序无法实现
  • 法一:快速统计出某个数前⾯有多少个数⽐它⼤

    • 在合并有序数组的时候,遇到左数组当前元素 > 右数组当前元素时,可以通过计算左数组中剩余元素的⻓度,就可快速求出右数组当前元素前⾯有多少个数⽐它⼤
      请添加图片描述
  • 法二:快速统计出某个数后⾯有多少个数⽐它⼩

    • 在合并有序数组的时候,遇到左数组当前元素 <= 右数组当前元素时,可以通过计算右数组已经遍历过的元素的⻓度,快速求出左数组当前元素后⾯有多少个数⽐它⼤
      请添加图片描述

3.代码实现

// v1.0 升序
class Solution 
{vector<int> assist;
public:int ReversePairs(vector<int>& nums) {assist.resize(nums.size());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) / 2;// 左边的个数 + 排序 + 右边的个数 + 排序// [left, mid] [mid + 1, right]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]){assist[i++] = nums[cur1++];}else{ret += mid - cur1 + 1;assist[i++] = nums[cur2++];}}// 处理未遍历完的数组while(cur1 <= mid){assist[i++] = nums[cur1++];}while(cur2 <= right){assist[i++] = nums[cur2++];}// 还原for(int i = left; i <= right; i++){nums[i] = assist[i - left];}return ret;}
};
----------------------------------------------------------------------------
// v2.0 降序
class Solution 
{vector<int> assist;
public:int reversePairs(vector<int>& nums) {assist.resize(nums.size());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) / 2;// 左边的个数 + 排序 + 右边的个数 + 排序// [left, mid] [mid + 1, right]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]){assist[i++] = nums[cur2++];}else{ret += right - cur2 + 1;assist[i++] = nums[cur1++];}}// 处理未遍历完的数组while(cur1 <= mid){assist[i++] = nums[cur1++];}while(cur2 <= right){assist[i++] = nums[cur2++];}// 还原for(int i = left; i <= right; i++){nums[i] = assist[i - left];}return ret;}
};

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

1.题目链接

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

2.算法原理详解

  • 思路:解法与「求数组中的逆序对」的解法类似,但是本题要返回⼀个数组,记录每⼀个元素的右边有多少个元素⽐⾃⼰⼩

  • 在归并排序的过程中,元素的下标是会跟着变化的

    • 因此需要⼀个辅助数组,来将数组元素和对应的下标绑定在⼀起归并
    • 也就是在归并元素的时候,顺势将下标也转移到对应的位置上
      请添加图片描述
  • 创建两个全局的数组

    • vector<int> ret -> 记录结果
    • vector<int> index -> 记录下标
      请添加图片描述

3.代码实现

class Solution 
{vector<int> ret;vector<int> index;vector<int> assistNums;vector<int> assistIndex;
public:vector<int> CountSmaller(vector<int>& nums) {int n = nums.size();ret.resize(n);index.resize(n);assistNums.resize(n);assistIndex.resize(n);// 初始化indexfor(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) / 2;// [left, mid] [mid + 1, right]// 先处理左右子数组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]){assistNums[i] = nums[cur2];assistIndex[i++] = index[cur2++];}else{ret[index[cur1]] += right - cur2 + 1; // 统计 -> 重点assistNums[i] = nums[cur1];assistIndex[i++] = index[cur1++];}}// 处理未遍历完数组while(cur1 <= mid){assistNums[i] = nums[cur1];assistIndex[i++] = index[cur1++];}while(cur2 <= right){assistNums[i] = nums[cur2];assistIndex[i++] = index[cur2++];}// 还原for(int i = left; i <= right; i++){nums[i] = assistNums[i - left];index[i] = assistIndex[i - left];}}
};

4.翻转对

1.题目链接

  • 翻转对

2.算法原理详解

  • 思路解析

    • ⼤思路与求逆序对的思路⼀样,利⽤归并排序的思想,将求整个数组的翻转对的数量,转换成三部分:
      • 左半区间翻转对的数量,右半区间翻转对的数量,⼀左⼀右选择时翻转对的数量
    • 重点就是在合并区间过程中,如何计算出翻转对的数量
      • 与上个问题不同的是,上⼀道题可以⼀边合并⼀遍计算,但是这道题要求的是左边元素⼤于右边元素的两倍,如果直接合并的话,是⽆法快速计算出翻转对的数量的
        请添加图片描述
  • 计算翻转对:利用单调性,使用同向双指针

    • 法一:计算当前元素后面,有多少元素的两倍比我小 -> 降序
    • 法二:计算当前元素之前,有多少元素的一半比我大 -> 升序

3.代码实现

// v1.0 降序
class Solution 
{vector<int> assist;
public:int reversePairs(vector<int>& nums) {assist.resize(nums.size());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) / 2;// [left, mid] [mid + 1, right]// 先计算左右子区间翻转对ret += MergeSort(nums, left, mid);ret += MergeSort(nums, mid + 1, right);// 计算一左一右翻转对的数量int cur1 = left, cur2 = mid + 1, i = 0;while(cur1 <= mid) // 降序 固定cur1{// * -> / 防溢出// / 2.0确保能除尽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){assist[i++] = nums[cur1] <= nums[cur2] ? nums[cur2++] : nums[cur1++];}// 处理未遍历完数组while(cur1 <= mid){assist[i++] = nums[cur1++];}while(cur2 <= right){assist[i++] = nums[cur2++];}// 还原for(int i = left; i <= right; i++){nums[i] = assist[i - left];}return ret;}
};
-------------------------------------------------------------------------
// v2.0 升序
class Solution 
{vector<int> assist;
public:int reversePairs(vector<int>& nums) {assist.resize(nums.size());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) / 2;// [left, mid] [mid + 1, right]// 先计算左右子区间翻转对ret += MergeSort(nums, left, mid);ret += MergeSort(nums, mid + 1, right);// 计算一左一右翻转对的数量int cur1 = left, cur2 = mid + 1, i = 0;while(cur2 <= right) // 升序  固定cur2{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){assist[i++] = nums[cur1] <= nums[cur2] ? nums[cur1++] : nums[cur2++];}// 处理未遍历完数组while(cur1 <= mid){assist[i++] = nums[cur1++];}while(cur2 <= right){assist[i++] = nums[cur2++];}// 还原for(int i = left; i <= right; i++){nums[i] = assist[i - left];}return ret;}
};

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

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

相关文章

利用RunnerGo数据大屏强化测试管理与决策

测试平台中的数据大屏在提供实时监控、统计分析、效率提升、制定策略和促进沟通等方面具有重要的意义。它为测试团队提供更全面、更直观的数据支持&#xff0c;有助于提高测试质量和效率&#xff0c;减少风险&#xff0c;并加强团队协作和沟通。 数据大屏也是RunnerGo的核心特…

用python写单链表

目录 链表的数据结构单链表操作头插入尾插入中间插入 删除查找 总结 链表的数据结构 链表是什么呢&#xff0c;来看下 链表&#xff0c;是一种数据结构。相对于数组而言&#xff0c;是不连续的一块内存空间。不仅如此&#xff0c;而且链表有多种&#xff0c;包括&#xff1a;…

Spring Security OAuth2 统一登录

介绍 Spring Security OAuth2 是一个在 Spring Security 框架基础上构建的 OAuth2 授权服务器和资源服务器的扩展库。它提供了一套功能强大的工具和组件&#xff0c;用于实现 OAuth2 协议中的授权流程、令牌管理和访问控制。 Git地址&#xff1a;yunfeng-boot3-sercurity: Sp…

火力发电厂超低排放标准?

目录&#xff1a; 引言火力发电厂超低排放的背景与意义火力发电厂超低排放环保指标解析环保验收要求超低排放的重要性环保验收的流程与要点超低排放技术措施与应用火力发电厂超低排放的监测与评估环保验收中常见问题及解决方案案例分析&#xff1a;成功实现超低排放的火力发电…

uniapp分包,以及通过uni-simple-router进行分包

先说一下uniapp的直接分包方式&#xff0c;很简单&#xff1a; 配置分包信息 打开manifest.json源码视图&#xff0c;添加 “optimization”:{“subPackages”:true} 开启分包优化 我们在根目录下创建一个pagesA文件夹&#xff0c;用来放置需要分包的页面 然后配置路由 运行到…

Unity3d 学习之按钮绑定事件

创建测试脚本 using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI;public class myTest : MonoBehaviour {// Start is called before the first frame updatepublic Button _codeBindBtn null;void Start(){if (_codeBi…

020Node.js的FS模块使用fs.mkdir创建目录

Node.js的FS模块使用fs.mkdir创建目录 //fs.mkdir 创建目录 /*path 将创建的目录路径mode 目录权限&#xff08;读写权限&#xff09;&#xff0c;默认777callback 回调&#xff0c;传递异常参数err*/ const fsrequire(fs);fs.mkdir(./css,(err)>{if(err){console.log(err)…

03.Kafka 基本使用

Kafka 提供了一系列脚本用于命令行来操作 kafka。 1 Topic 操作 1.1 创建 Topic 创建一个名为 oldersix-topic 的 topic&#xff0c;副本数设置为3&#xff0c;分区数设置为2&#xff1a; bin/kafka-topics.sh \ --create \ --zookeeper 192.168.31.162:2181 \ --replication…

深度学习之基于多模态融合的商品分类方法研究与实现

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 项目简介&#xff1a;深度学习之基于多模态融合的商品分类方法研究与实现 一、项目背景与目标 随着电子商务的快速…

Polyscope,一款简洁的三维可视化工具!

Polyscope是用于三维数据&#xff08;如meshes、point clouds&#xff09;的可视化工具&#xff0c;通过编程或动态GUI完成&#xff1b;支持C和Python编程&#xff1b;追求“一行代码”为数据提供有用的可视界面展示。 下面来简单介绍Polyscope使用。 Polyscope效果 Point Cl…

【深度学习】【Lora训练1】StabelDiffusion,Lora训练过程,秋叶包,Linux,SDXL Lora训练

文章目录 一、环境搭建指南二、个性化安装流程三、启动应用四、打开web五、开始训练 19.27服务器 一、环境搭建指南 打造一个高效且友好的开发环境&#xff1a; 项目源码获取&#xff1a; 通过以下命令轻松克隆项目及所有子模块至您的Linux系统&#xff1a; git clone --recu…

主观赋权法、客观赋权法、组合赋权法、评价指标体系构建

在科研领域&#xff0c;为了对某个研究主题进行深入的探讨和评估&#xff0c;我们往往需要构建一套科学合理的评价体系&#xff0c;并为其中的各项评价指标赋予相应的权重。比如&#xff0c;在评价一项新技术的性能时&#xff0c;我们可能会考虑其创新性、实用性、成本效益等多…

牛客NC242 单词搜索【中等 递归DFS C++/Java/Go/PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/987f2981769048abaf6180ed63266bb2 思路 递归&#xff1a;以word第一个字符为起点&#xff0c;在矩阵中 递归搜索&#xff0c;检查是否存在完整的word路径&#xff0c; 注意恢复现场&#xff0c;又叫回溯&#…

【LAMMPS学习】八、基础知识(5.1)有限尺寸球形和非球形粒子

8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语&#xff0c;以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各…

Gin的中间件执行流程与用法

一、背景 我们在使用Gin框架进行Web开发的时候&#xff0c;基本上都会遇到登录拦截的场景。 例如某些接口必须在登录以后才能访问&#xff0c;根据登录用户的信息以及权限&#xff0c;拿到属于自己的数据, 反之&#xff0c;没登录过则直接拒绝访问。 那么我们怎么做到这些登录…

Agent AI 智能体的未来

Agent AI智能体的未来可能会变得更加普遍和智能。以下是一些可能出现的趋势和发展方向&#xff1a; 1、个性化服务: Agent AI智能体可能会变得更加个性化&#xff0c;能够根据用户的偏好和习惯提供定制化的服务和建议。 2、多模态交互: 未来的Agent AI可能会支持更多的多模态交…

【麒麟(Linux)系统远程连接到windows系统并进行文件传输】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言使用步骤总结 前言 一般来说&#xff0c;windows自带远程桌面&#xff0c;使用的RDP协议&#xff0c;Linux上支持RDP协议的软件很多&#xff0c;常用的是Remmi…

陪丨玩丨系丨统搭建制作流程APP小程序H5多端源码前后端一次性交付,本地授权,无二次费用!可定制开发!

陪丨玩app小程序H5开发&#xff0c;软件搭建&#xff0c;程序制作、系统设计 数据存储是陪玩平台源码的重点&#xff0c;没有数据库&#xff0c;用户的账号信息、平台产生的数据都无法顺利存储和读取&#xff0c;不能让用户拥有完善良好的用户体验。虽然是存放在服务器上&…

ThreeJs 环境配置及遇到问题的解决方法

一、环境搭建 ThreeJs在实际在实际使用中更多的是结合框架开发例如&#xff1a;vue框架、react框架&#xff0c;在使用时需要配置开发环境&#xff0c;本文使用的是vscode ThreeJs NodeJs vue 1、ThreeJs安装 下载路径&#xff1a;GitHub - mrdoob/three.js: JavaScript…

jbutton 按钮 圆角 渐变色

效果图 没按压时背景渐变色 鼠标按压时背景色改变 下面是可用的源码 package com.beijin.robot.utils; import javax.swing.*; import javax.swing.border.Border; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent;public clas…