【C++算法】50.分治_归并_翻转对

文章目录

    • 题目链接:
    • 题目描述:
    • 解法
    • C++ 算法代码:
    • 图解


题目链接:

493. 翻转对


题目描述:

e1a56c57c284100520d1840d3224460e


解法

分治

策略一:计算当前元素cur1后面,有多少元素的两倍比我cur1小(降序)

利用单调性使用同向双指针。

6ca12f25aadf9a660c566e1254f800d0

策略二:计算当前元素cur2之前,有多少元素的一半比我cur2大(升序)

8ae918bf3c685ed0b37605b78c2cc096

最后合并两个有序数组。


C++ 算法代码:

降序版本

class Solution 
{int tmp[50010];  // 临时数组,用于归并排序中合并两个子数组
public:// 主函数,计算数组中的翻转对数量int reversePairs(vector<int>& nums) {return mergeSort(nums, 0, nums.size() - 1);  // 调用归并排序函数}// 归并排序函数,返回区间[left, right]内的翻转对数量int mergeSort(vector<int>& nums, int left, int right){if(left >= right) return 0;  // 基本情况:如果区间只有一个元素或为空,翻转对数量为0int ret = 0;  // 保存翻转对数量// 1. 先根据中间元素划分区间int mid = (left + right) >> 1;  // 计算中间位置,相当于 (left + right) / 2// 2. 递归计算左右两侧的翻转对数量ret += mergeSort(nums, left, mid);       // 左半部分翻转对数量ret += mergeSort(nums, mid + 1, right);  // 右半部分翻转对数量// 3. 计算跨越左右两个子数组的翻转对数量int cur1 = left, cur2 = mid + 1;// 对于左子数组中的每个元素,计算右子数组中有多少元素可以构成翻转对while(cur1 <= mid) {// 在右子数组中查找第一个小于 nums[cur1]/2 的元素位置// 即满足 nums[cur2] < nums[cur1]/2 的最小的cur2while(cur2 <= right && nums[cur2] >= nums[cur1] / 2.0) cur2++;if(cur2 > right)  // 如果右子数组中所有元素都不满足条件break;// 右子数组中从cur2到right的所有元素都能与nums[cur1]构成翻转对ret += right - cur2 + 1;cur1++;  // 处理左子数组中的下一个元素}// 4. 合并两个有序子数组(降序合并)cur1 = left;cur2 = mid + 1;int i = left;  // 临时数组的起始索引// 比较左右子数组元素,较大者先放入临时数组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;  // 返回翻转对总数}
};

升序版本

class Solution 
{int tmp[50010];  // 临时数组,用于归并排序中合并两个子数组
public:// 主函数,计算数组中的翻转对数量int reversePairs(vector<int>& nums) {return mergeSort(nums, 0, nums.size() - 1);  // 调用归并排序函数}// 归并排序函数,返回区间[left, right]内的翻转对数量int mergeSort(vector<int>& nums, int left, int right){if(left >= right) return 0;  // 基本情况:如果区间只有一个元素或为空,翻转对数量为0int ret = 0;  // 保存翻转对数量// 1. 先根据中间元素划分区间int mid = (left + right) >> 1;  // 计算中间位置,相当于 (left + right) / 2// 2. 递归计算左右两侧的翻转对数量ret += mergeSort(nums, left, mid);       // 左半部分翻转对数量ret += mergeSort(nums, mid + 1, right);  // 右半部分翻转对数量// 3. 计算跨越左右两个子数组的翻转对数量int cur1 = left, cur2 = mid + 1;// 对于右子数组中的每个元素,计算左子数组中有多少元素可以构成翻转对while(cur2 <= right) {// 在左子数组中查找第一个大于 2*nums[cur2] 的元素位置// 即满足 nums[cur1] > 2*nums[cur2] 的最小的cur1while(cur1 <= mid && nums[cur2] >= nums[cur1] / 2.0) cur1++;if(cur1 > mid)  // 如果左子数组中所有元素都不满足条件break;// 左子数组中从cur1到mid的所有元素都能与nums[cur2]构成翻转对ret += mid - cur1 + 1;cur2++;  // 处理右子数组中的下一个元素}// 4. 合并两个有序子数组(升序合并)cur1 = left;cur2 = mid + 1;int i = left;  // 临时数组的起始索引// 比较左右子数组元素,较小者先放入临时数组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;  // 返回翻转对总数}
};

图解

例如:nums = [1, 3, 2, 3, 1]

初始化:

  • 原始数组: [1, 3, 2, 3, 1]
  • 我们需要找到所有满足 i < jnums[i] > 2 * nums[j] 的索引对 (i, j)

第一层递归(整个数组):

  • [1, 3, 2, 3, 1] 分为 [1, 3][2, 3, 1]

处理左半部分 [1, 3]:

  • 进一步划分为 [1][3]
  • 这些是单个元素,不需要计算内部的翻转对
  • 计算跨越的翻转对: 没有跨越的翻转对(因为两个子数组只有一个元素)
  • 合并: [3, 1] (降序排序)

处理右半部分 [2, 3, 1]:

  • 进一步划分为 [2][3, 1]
  • 对于 [3, 1], 递归处理:
    • 划分为 [3][1]
    • 计算跨越的翻转对: 3 > 2*1, 所以有1个翻转对
    • 合并: [3, 1] (降序排序)
  • 计算跨越的翻转对([2] 和 [3, 1]):
    • 对于左子数组的元素2:
      • 检查右子数组的元素: 2 不大于 2*3, 但2 > 2*1
      • 所以有1个翻转对
  • 合并: [3, 2, 1] (降序排序)

最后合并 [3, 1][3, 2, 1]:

  • 计算跨越的翻转对:
    • 对于左子数组的元素3:
      • 检查右子数组的元素: 3 不大于 2*3, 3 不大于 2*2, 但3 > 2*1
      • 所以有1个翻转对
    • 对于左子数组的元素1:
      • 检查右子数组的元素: 1 不大于 2*3, 1 不大于 2*2, 1 不大于 2*1
      • 所以没有翻转对
  • 合并: [3, 3, 2, 1, 1] (降序排序)

计算翻转对总数:

  • 左半部分内部: 0个
  • 右半部分内部: 1+1=2个
  • 跨越左右半部分: 0个

因此,翻转对总数是 0 + 2 + 0 = 2 个。

这两个翻转对分别对应原数组中的:

  1. (1, 4): 索引1处的元素3 > 2*索引4处的元素1
  2. (3, 4): 索引3处的元素3 > 2*索引4处的元素1

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

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

相关文章

深入讲解:智能合约中的读写方法

前言 在探秘区块链开发:智能合约在 DApp 中的地位及与传统开发差异一文中我提到对于智能合约中所有的写入其实都算是交易。而在一个完整的智能合约代码中最大的两个组成部分就是读取和写入。 本文将为你深入探讨该两者方法之间的区别。 写方法 写方法其实就是对区块链这一…

Go语言类型捕获及内存大小判断

代码如下&#xff1a; 类型捕获可使用&#xff1a;reflect.TypeOf()&#xff0c;fmt.Printf在的%T。 内存大小判断&#xff1a;len()&#xff0c;unsafe.Sizeof。 package mainimport ("fmt""unsafe""reflect" )func main(){var i , j 1, 2f…

MyBatis Plus 在 ZKmall开源商城持久层的优化实践

ZKmall开源商城作为基于 Spring Cloud 的高性能电商平台&#xff0c;其持久层通过 MyBatis Plus 实现了多项深度优化&#xff0c;涵盖分库分表、缓存策略、分页性能、多租户隔离等核心场景。以下是具体实践总结&#xff1a; 一、分库分表与插件集成优化 1. 分库分表策略 ​Sh…

学习MySQL第七天

夕阳无限好 只是近黄昏 一、子查询 1.1 定义 将一个查询语句嵌套到另一个查询语句内部的查询 我们通过具体示例来进行演示&#xff0c;这一篇博客更侧重于通过具体的小问题来引导大家独立思考&#xff0c;然后熟悉子查询相关的知识点 1.2 问题1 谁的工资比Tom高 方…

Nginx 常见面试题

一、nginx常见错误及处理方法 1.1 404 bad request 一般原因&#xff1a;请求的Header过大 解决办法&#xff1a; 配置nginx.conf 相关设置1. client_header_buffer_size 16k; 2. large_client_header_buffers 4 64k;1.2 413 Request Entity Too Large 一般原因&#xff1…

LeetCode 每日一题 2025/3/31-2025/4/6

记录了初步解题思路 以及本地实现代码&#xff1b;并不一定为最优 也希望大家能一起探讨 一起进步 目录 3/31 2278. 字母在字符串中的百分比4/1 2140. 解决智力问题4/2 2873. 有序三元组中的最大值 I4/3 2874. 有序三元组中的最大值 II4/4 1123. 最深叶节点的最近公共祖先4/5 1…

Docker Compose 常用命令 运行 docker-compose.yaml

Docker Compose 中有两个重要的概念 服务 (service)&#xff1a;一个应用的容器&#xff0c;实际上可以包括若干运行相同镜像的容器实例。 项目 (project)&#xff1a;由一组关联的应用容器组成的一个完整业务单元&#xff0c;在 docker-compose.yml 文件中定义。 为了更方便…

深度学习中的 Batch 机制:从理论到实践的全方位解析

一、Batch 的起源与核心概念 1.1 批量的中文译名解析 Batch 在深度学习领域标准翻译为"批量"或"批次"&#xff0c;指代一次性输入神经网络进行处理的样本集合。这一概念源自统计学中的批量处理思想&#xff0c;在计算机视觉先驱者Yann LeCun于1989年提出…

Unity Internal-ScreenSpaceShadows 分析

一、代码结构 // Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)Shader "Hidden/Internal-ScreenSpaceShadows" {Properties {_ShadowMapTexture ("", any) "" {} // 阴影贴图纹理&…

Token+JWT+Redis 实现鉴权机制

TokenJWTRedis 实现鉴权机制 使用 Token、JWT 和 Redis 来实现鉴权机制是一种常见的做法&#xff0c;尤其适用于分布式应用或微服务架构。下面是一个大致的实现思路&#xff1a; 1. Token 和 JWT 概述 Token&#xff1a;通常是一个唯一的字符串&#xff0c;可以用来标识用户…

RPC与其他通信技术的区别,以及RPC的底层原理

1、什么是 RPC&#xff1f; 远程过程调用&#xff08;RPC&#xff09; 是一种协议&#xff0c;它允许程序在不同计算机之间进行通信&#xff0c;让开发者可以像调用本地函数一样发起远程请求。 通过 RPC&#xff0c;开发者无需关注底层网络细节&#xff0c;能够更专注于业务逻…

简洁的 PlantUML 入门教程

评论中太多朋友在问&#xff0c;我的文章中图例如何完成的。 我一直用plantUML,也推荐大家用&#xff0c;下面给出一个简洁的PlantUML教程。 &#x1f331; 什么是 PlantUML&#xff1f; PlantUML 是一个用纯文本语言画图的工具&#xff0c;支持流程图、时序图、用例图、类图、…

互联网三高-高性能之JVM调优

1 运行时数据区 JVM运行时数据区是Java虚拟机管理的内存核心模块&#xff0c;主要分为线程共享和线程私有两部分。 &#xff08;1&#xff09;线程私有 ① 程序计数器&#xff1a;存储当前线程执行字节码指令的地址&#xff0c;用于分支、循环、异常处理等流程控制‌ ② 虚拟机…

浅谈StarRocks 常见问题解析

StarRocks数据库作为高性能分布式分析数据库&#xff0c;其常见问题及解决方案涵盖环境部署、数据操作、系统稳定性、安全管控及生态集成五大核心领域&#xff0c;需确保Linux系统环境、依赖库及环境变量配置严格符合官方要求以避免节点启动失败&#xff0c;数据导入需遵循格式…

P1332 血色先锋队(BFS)

题目背景 巫妖王的天灾军团终于卷土重来&#xff0c;血色十字军组织了一支先锋军前往诺森德大陆对抗天灾军团&#xff0c;以及一切沾有亡灵气息的生物。孤立于联盟和部落的血色先锋军很快就遭到了天灾军团的重重包围&#xff0c;现在他们将主力只好聚集了起来&#xff0c;以抵…

大文件上传之断点续传实现方案与原理详解

一、实现原理 文件分块&#xff1a;将大文件切割为固定大小的块&#xff08;如5MB&#xff09; 进度记录&#xff1a;持久化存储已上传分块信息 续传能力&#xff1a;上传中断后根据记录继续上传未完成块 块校验机制&#xff1a;通过哈希值验证块完整性 合并策略&#xff1a;所…

【动手学深度学习】卷积神经网络(CNN)入门

【动手学深度学习】卷积神经网络&#xff08;CNN&#xff09;入门 1&#xff0c;卷积神经网络简介2&#xff0c;卷积层2.1&#xff0c;互相关运算原理2.2&#xff0c;互相关运算实现2.3&#xff0c;实现卷积层 3&#xff0c;卷积层的简单应用&#xff1a;边缘检测3.1&#xff0…

Opencv计算机视觉编程攻略-第十一节 三维重建

此处重点讨论在特定条件下&#xff0c;重建场景的三维结构和相机的三维姿态的一些应用实现。下面是完整投影公式最通用的表示方式。 在上述公式中&#xff0c;可以了解到&#xff0c;真实物体转为平面之后&#xff0c;s系数丢失了&#xff0c;因而无法会的三维坐标&#xff0c;…

大厂不再招测试?软件测试左移开发合理吗?

&#x1f449;目录 1 软件测试发展史 2 测试左移&#xff08;Testing shift left&#xff09; 3 测试右移&#xff08;Testing shift right&#xff09; 4 自动化测试 VS 测试自动化 5 来自 EX 测试的寄语 最近两年&#xff0c;互联网大厂的招聘中&#xff0c;测试工程师岗位似…

windows10下PointNet官方代码Pytorch实现

PointNet模型运行 1.下载源码并安装环境 GitCode - 全球开发者的开源社区,开源代码托管平台GitCode是面向全球开发者的开源社区,包括原创博客,开源代码托管,代码协作,项目管理等。与开发者社区互动,提升您的研发效率和质量。https://gitcode.com/gh_mirrors/po/pointnet.pyto…