目录

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

1.1 题目解析

1.2 解法

1.3 代码实现

2. 翻转对

2.1 题目解析

2.2 解法

2.3 代码实现


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

315. 计算右侧小于当前元素的个数 - 力扣(LeetCode)

给你一个整数数组 nums ,按要求返回一个新数组 counts 。数组 counts 有该性质: counts[i] 的值是  nums[i] 右侧小于 nums[i] 的元素的数量。

示例 1:

输入:nums = [5,2,6,1]
输出:[2,1,1,0] 
解释:
5 的右侧有 2 个更小的元素 (2 和 1)
2 的右侧仅有 1 个更小的元素 (1)
6 的右侧有 1 个更小的元素 (1)
1 的右侧有 0 个更小的元素

示例 2:

输入:nums = [-1]
输出:[0]

示例 3:

输入:nums = [-1,-1]
输出:[0,0]

提示:

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104

1.1 题目解析

  • 题目本质

    对每个位置 i,统计它右侧有多少元素 < nums[i]。这是“离线统计 + 有序累积”的典型问题:如果能让“右侧区域”保持有序,那么当一个左侧元素要“落位”时,就能一次性把比它小的右侧元素数目加上去。

  • 常规解法:对每个 i 向右扫一遍计数,或把每个 i 与其右边所有元素比较。
    复杂度 O(n²),n 最多 1e5,超时。

  • 问题分析:O(n²) 不行;要降低到 O(n log n)。要么用“平衡树/树状数组/线段树”边插边查,要么在“归并排序”的有序合并过程中顺手统计。

  • 思路转折:用 归并排序(分治)。关键是不直接搬值,而是搬“原始下标”

    • 比较用 nums[index[*]];

    • 计数写回 counts[index[*]]。
      这样既能用数值排队,又不丢原始位置,统计才能落对人。

1.2 解法

算法思想
维护一个下标数组 index,对下标做归并:

        降序归并 + 一次性加法:左右两段都保持降序。若 nums[left] > nums[right],则右半段从 cur2 到 right 的所有元素都更小,直接 += (right - cur2 + 1) 并把左元素出列;否则先出右元素。

i)初始化:index[i]=i,counts 全 0,准备 tmp 临时数组。

ii)分治:mergeSort(nums, left, right)。

iii)合并:

  • 指针 cur1 =left, cur2 =mid+1,k 写入 tmp。

  • 若 nums[index[cur1]] <= nums[index[cur2]]
    tmp[k++]=index[cur2++]。

  • 否则:counts[index[cur1]] += (right - j + 1)
    tmp[k++]=index[cur1++]。

  • 扫尾:
    while (cur1 <= mid) tmp[k++] = index[cur1++];
    while (cur2 <= right) tmp[k++] = index[cur2++];

  • 回写:
    for (int j = left; j <= right; j++) index[j] = tmp[j - left];

iiii)返回 counts 的列表形式。

易错点

  • 只搬下标,不搬值:比较写 nums[index[*]],统计写 counts[index[*]]。

  • 回写的是 index,不是 nums:index[j]=tmp[j-left] 或让 k 从 left 开始写

  • 注意 while 循环里比大小时判断的条件

  • 多层归并要累加:counts[...] += ...,不能覆盖。

  • tmp 不是答案数组,它只是合并时的 辅助排序区,归并用的临时缓冲区,帮助我们把当前区间 [left..right] 合并成有序;排序这一步是必须的,因为我们借助“有序”的过程,才能在 O(n log n) 里顺手统计“右边更小”的个数。

  • counts:是真正的答案数组,counts[cur1] 表示 nums[cur1] 右侧比它小的数量,最后返回它即可。

  • k++ 只是为了推进左半区的扫描,别忘了这个数组是记录下标而不是原始数组的值,所以要推进下标数组的下标值。

  • index 数组的作用是“保持数值和原始下标之间的映射关系不丢失”。

1.3 代码实现

import java.util.*;class Solution {int[] index;   // 记录每个元素的“原始下标”,在归并过程中只搬动下标int[] tmp;     // 临时数组,用来辅助归并排序(保存下标,不保存值)int[] counts;  // 结果数组,counts[i] = nums[i] 右侧比它小的数量public List<Integer> countSmaller(int[] nums) {int n = nums.length;tmp = new int[n];counts = new int[n];index = new int[n];// 初始化下标数组,初始时 index[i] = ifor (int i = 0; i < n; i++) {index[i] = i;}// 分治归并排序mergeSort(nums, 0, n - 1);// 把 counts 数组转成 List<Integer> 返回List<Integer> list = new ArrayList<>(n);for (int v : counts) list.add(v);return list;}public void mergeSort(int[] nums, int left, int right) {if (left >= right) return;  // 递归基:区间只有一个元素时结束int mid = left + (right - left) / 2;// 递归排序左半区mergeSort(nums, left, mid);// 递归排序右半区mergeSort(nums, mid + 1, right);int cur1 = left, cur2 = mid + 1, k = 0;// 合并两个有序区间(这里用“降序”方式)while (cur1 <= mid && cur2 <= right) {if (nums[index[cur1]] <= nums[index[cur2]]) {// 如果右边的值更大或相等,先把右边的下标放进 tmptmp[k++] = index[cur2++];} else {// 如果左边的值更大:// 说明右边 [cur2..right] 的元素全都比它小counts[index[cur1]] += (right - cur2 + 1);// 把这个左边元素的下标放进 tmptmp[k++] = index[cur1++];}}// 把左半区剩余元素拷贝进 tmpwhile (cur1 <= mid) tmp[k++] = index[cur1++];// 把右半区剩余元素拷贝进 tmpwhile (cur2 <= right) tmp[k++] = index[cur2++];// 回写:把 tmp 里的结果拷回到 index 数组对应区间for (int j = left; j <= right; j++) {index[j] = tmp[j - left];}}
}

复杂度分析

  • 时间:分治 + 合并为 O(n log n),每层合并线性。

  • 空间:index/tmp/counts 为 O(n),递归栈 O(log n)。

2. 翻转对

493. 翻转对 - 力扣(LeetCode)

给定一个数组 nums ,如果 i < j 且 nums[i] > 2*nums[j] 我们就将 (i, j) 称作一个重要翻转对

你需要返回给定数组中的重要翻转对的数量。

示例 1:

输入: [1,3,2,3,1]
输出: 2

示例 2:

输入: [2,4,3,5,1]
输出: 3

注意:

  1. 给定数组的长度不会超过50000
  2. 输入数组中的所有数字都在32位整数的表示范围内。

2.1 题目解析

  • 题目本质:统计所有满足 i < j 且 nums[i] > 2 * nums[j] 的二元组数量。这是一个“离线统计 + 有序累积”的经典范式:如果左右两段各自有序,就能用双指针线性地数出跨段满足条件的配对数量。

  • 常规解法:双重循环枚举 (i, j),逐对判断是否 nums[i] > 2*nums[j]。时间复杂度 O(n^2),n 最多可到 5e4,显然超时。

  • 问题分析:要把复杂度压到 O(n log n)。两条常见路径:
    1)平衡树 / 树状数组 / 线段树做“边插边查”;
    2)分治归并:在“合并”前用双指针数跨段配对,再做标准归并。

  • 思路转折:采用分治归并。关键点:

    • 先数对,后归并:当左右段([left..mid] 与 [mid+1..right])各自已升序时,固定左段的 i,右段用 j 单调右移,线性统计满足 nums[i] > 2*nums[j] 的个数。

    • 用 long 比较避免溢出::写成 (long)nums[i] > 2L * (long)nums[j],不要用 /2.0 的浮点比较。

    • 归并阶段使用标准升序合并,保持下一层统计的前提(左右各自升序)。

2.2 解法

算法思想

分治:mergeSort(left, right)。在回溯(合并)前,借助“两侧各自升序”,对每个 i ∈ [left..mid],移动 j 直到不满足条件为止,累计 j - (mid + 1)。再做一次标准升序归并,保证整体有序供更高层使用。

i)递归到底:left >= right 返回。

ii)递归左右:mergeSort(left, mid)mergeSort(mid+1, right)

iii)先统计

  • j = mid + 1;

  • 对每个 i = left..mid:while (j <= right && (long)nums[i] > 2L * (long)nums[j]) j++;

  • 累加 result += (j - (mid + 1))。

iiii)再归并(升序):常规双指针把 [left..mid] 与 [mid+1..right] 合并到 tmp,再回写。

iiiii)返回最终 result。

易错点

  • 不要边合并 边用 (right - cur2 + 1) 一把加:那是普通逆序对 nums[i] > nums[j] 的套路,对 > 2*nums[j] 不成立。

  • 比较必须用 long:2 * nums[j] 在 int 里可能溢出。

  • 不要用/2.0:浮点精度 + 负数边界容易出错,也不必要。

  • 双计数陷阱:要么用全局 result,要么函数返回值累计,二者不要混用

  • 合并要升序:保证下一层“先数对”的前提(左右各自已升序)。

  • 指针单调:统计阶段 j 只右移不回退,整体线性。

2.3 代码实现

import java.util.*;class Solution {int[] tmp;int result;public int reversePairs(int[] nums) {tmp = new int[nums.length];mergeSort(nums, 0, nums.length - 1);return result;}// 只做副作用统计与排序,不用返回值累计public void mergeSort(int[] nums, int left, int right){if (left >= right) return;int mid = left + (right - left) / 2;mergeSort(nums, left, mid);mergeSort(nums, mid + 1, right);// 先统计:左右两段各自已升序int j = mid + 1;for (int i = left; i <= mid; i++) {while (j <= right && (long)nums[i] > 2L * (long)nums[j]) j++;// 右半段 [mid+1, j-1] 都满足 nums[i] > 2*nums[?]result += (j - (mid + 1));}// 再归并:标准升序合并int cur1 = left, cur2 = mid + 1, k = 0;while (cur1 <= mid && cur2 <= right) {if (nums[cur1] <= nums[cur2]) {tmp[k++] = nums[cur1++];} else {tmp[k++] = nums[cur2++];}}while (cur1 <= mid)  tmp[k++] = nums[cur1++];while (cur2 <= right) tmp[k++] = nums[cur2++];// 回写for (int t = 0; t < k; t++) {nums[left + t] = tmp[t];}}
}

复杂度分析

  • 时间:每层“先数对”用两个单调指针线性扫描 O(n),合并 O(n),层数 O(log n),总体 O(n log n)。

  • 空间:辅助数组 tmp O(n),递归栈 O(log n)。

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

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

相关文章

基于SamOut的音频Token序列生成模型训练指南

通过PyTorch实现从音频特征到语义Token的端到端序列生成&#xff0c;适用于语音合成、游戏音效生成等场景。&#x1f9e0; 模型架构与核心组件 model SamOut(voc_sizevoc_size, # 词汇表大小&#xff08;4098目录名特殊Token&#xff09;hidden_sizehidden_size, …

AWD攻防总结

基本防守策略 1、改用户密码和服务密码 1&#xff09;改linux用户密码&#xff1a; #passwd 如果有权限就删除用户&#xff1a; #userdel -r [用户名] 2&#xff09;改mysql密码&#xff1a; #update mysql.user set passwordpassword(密码) where userroot; 删除匿名用户&…

Android14 基于Configfs的USB动态配置init.usb.configfs.rc

1 Android14 USB子系统启动以及动态切换的init.usb.rc 2 Android14 基于Configfs的USB动态配置init.usb.configfs.rc 3 Android14 高通平台的USB子系统启动和动态配置init.qcom.usb.rc 1. 什么是ConfigFS ConfigFS 是 Linux 内核提供的一种用户空间可配置的伪文件系统在Linu…

2025年KBS SCI1区TOP,矩阵差分进化算法+移动网络视觉覆盖无人机轨迹优化,深度解析+性能实测

目录1.摘要2.系统模型和问题表述3.矩阵差分进化算法4.结果展示5.参考文献6.算法辅导应用定制读者交流1.摘要 本文提出了一种面向无人机&#xff08;UAV&#xff09;新型轨迹优化方法&#xff0c;以实现对地面移动节点的高效视觉覆盖。与传统方法不同&#xff0c;该方法显式考虑…

Python OpenCV图像处理与深度学习:Python OpenCV图像几何变换入门

图像变换&#xff1a;掌握OpenCV中的几何变换 学习目标 通过本课程&#xff0c;学员们将能够理解图像的几何变换原理&#xff0c;包括缩放、旋转和平移&#xff0c;并能够使用Python和OpenCV库实现这些变换。本课程将通过理论讲解与实践操作相结合的方式&#xff0c;帮助学员们…

Redis Windows 7.0.5 安装教程(附exe/msi下载+环境配置+命令测试)

​第一步&#xff1a;下安装包​ 打开浏览器&#xff08;比如 Edge 或 Chrome&#xff09;&#xff0c;复制这个链接到地址栏敲回车&#xff1a; https://pan.quark.cn/s/31912e0d0443 进去后往下翻&#xff0c;找名字带 ​**redis-7.0.5​ 的文件&#xff0c;​选那个 .exe 结…

数据结构(单链表)

目录 1.链表的概念及结构 2.单链表的应用 2.1 打印链表 2.2申请新节点 2.3插入&#xff08;尾删和头删&#xff09; 2.4删除&#xff08;尾删和头删&#xff09; 2.5查找 2.6任意位置插入 2.7删除指定位置的元素 2.8 销毁链表 3.总结 1.链表的概念及结构 &#xff…

电脑没加域却能获取到IP地址

企业网络管理的核心逻辑&#xff01;电脑没加域却能获取到IP地址&#xff0c;这完全是一种刻意为之的安全设计&#xff0c;而不是网络故障。 简单来说就是&#xff1a;“给你IP&#xff0c;但不给你权限。” 这背后是一套完整的 网络准入控制&#xff08;NAC&#xff09; 策略。…

Go语言入门学习笔记

&#x1f4da; 前言 欢迎学习Go语言&#xff01;这份教材假设您是编程零基础&#xff0c;从最基本的概念开始讲解。Go语言&#xff08;也称为Golang&#xff09;由Google开发&#xff0c;简单、高效、并发能力强&#xff0c;适合后端开发、系统编程和云计算。 学习建议&#xf…

gradle安装、配置环境变量、配置阿里源及idea 中配置gradle

下载gradle https://services.gradle.org/distributions/ 配置系统环境变量 新增GRADLE_HOME D:\Information_Technology\App\gradle-8.14.3-bin\gradle-8.14.3 新增GRADLE_USER_HOME D:\Information_Technology\App\gradleHouse 设置 path&#xff0c;新增一行 %GRADLE_…

C# FlaUI win 自动化框架,介绍

一、简洁介绍 FlaUI 是一套基于 .NET 的 Windows 桌面应用自动化测试库&#xff0c;支持 Win32、WinForms、WPF、UWP 等多种类型的应用。它基于微软原生 UI Automation 库&#xff0c;提供了更现代、易用的 API&#xff0c;适合自动化测试工程师和开发者实现高效、可维护的 UI …

命名空间级别应用 Pod 安全标准

&#x1f3af; 命名空间级别应用 Pod 安全标准 一、创建 Kubernetes 集群&#xff08;使用 kind&#xff09; 使用 kind &#xff08;Kubernetes IN Docker&#xff09;快速创建一个本地集群&#xff1a; kind create cluster --name my-cluster验证集群是否运行正常&#xff1…

Ubuntu 25.10 Snapshot4 发布。

Ubuntu 25.10 的第四个快照&#xff08;Snapshot 4&#xff09;已于 2025 年 8 月 28 日发布&#xff0c;供开发者和测试人员进行验证。这是 Ubuntu 25.10 正式发布前的最后一个月度快照&#xff0c;标志着该版本已进入功能冻结阶段&#xff0c;预计将在 10 月发布正式版。 Ca…

STM32F2/F4系列单片机解密和芯片应用介绍

STM32F2/F4系列单片机解密和芯片应用介绍STM32F2和STM32F4系列微控制器凭借其出色的性能、丰富的外设接口和强大的连接能力&#xff0c;在很多对计算能力和实时性有要求的领域都有应用。同时&#xff0c;芯片解密的价格因其型号、加密技术等因素差异较大。&#x1f9ed; 重要提…

250901-BookStack跨服务器从Rootless-Docker到Rootful-Docker的备份迁移及服务启动

下面给你一套「可离线、最小停机」的迁移步骤&#xff0c;从 A&#xff08;rootless&#xff09;搬到 B&#xff08;rootful&#xff09;。思路是&#xff1a;停 A → 打包数据卷 → 传到 B → 还原 → 用同版本镜像启动 → 验证。整套操作不依赖公网&#xff0c;只用你已有的离…

(Redis)Redis 分布式锁及改进策略详解

一、为什么需要分布式锁在单机应用中&#xff0c;synchronized 或 ReentrantLock 足以解决并发问题。但在 分布式系统 中&#xff0c;多台服务器之间共享同一个资源时&#xff0c;如果没有锁&#xff0c;很可能出现 超卖、重复扣减、数据不一致 等问题。 因此&#xff0c;分布式…

Linux应用开发-windows,linux环境下相关工具

VS Code Remote - SSH 虚拟机部分的操作 sudo systemctl status sshsudo apt update sudo apt install openssh-server sudo systemctl start ssh sudo systemctl enable ssh # 设置开机自启hostname -IVS Code部分的操作 安装 Remote - SSH 插件 vscode右下角出现&#xff…

Java泛型通配符详解:搞懂?/extends/super用法,避开集合操作踩坑点

上次跟你们聊了泛型的基础用法&#xff0c;今天接着往下说 —— 泛型里还有个挺重要的概念叫 “通配符”&#xff0c;就是那个问号 “?”&#xff0c;很多人第一次见都懵&#xff1a;这玩意儿跟普通泛型有啥区别&#xff1f;为啥有时候非得用它不可&#xff1f;小索奇当初也卡…

EXCEL开发之路(二)跨表交互模拟—仙盟创梦IDE

在车辆租赁行业&#xff0c;数据的高效管理与分析对于企业的运营决策、资源调配及客户服务优化至关重要。自建 Excel 实现多表统计交互&#xff0c;如同为行业装上了效能驱动引擎&#xff0c;助力企业在复杂多变的市场环境中稳健前行。一、精准资源管理&#xff0c;优化车辆调配…

医疗AI时代的生物医学Go编程:高性能计算与精准医疗的案例分析(八)

5.4 性能测试与结果分析 为了评估GoEHRStream的性能,我们设计测试模拟真实的医院数据流场景,并测量关键指标。 5.4.1 实验环境 硬件: CPU: Intel Xeon E-2288G (8 cores, 16 threads) RAM: 32 GB DDR4 Storage: 512 GB NVMe SSD (用于GoEHRStream和BadgerDB) Network: 1 G…