分治算法(7)_归并排序_计算右侧小于当前元素的个数

news2024/10/9 20:56:53

个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

分治算法(7)_归并排序_计算右侧小于当前元素的个数

收录于专栏【经典算法练习】
本专栏旨在分享学习算法的一点学习笔记,欢迎大家在评论区交流讨论💌

目录

温馨提示: 

1. 题目链接

2. 题目描述

3. 解法

算法思路:

代码展示:


温馨提示: 

这一道题的解法与求数组中的逆序对的解法是类似的, 但是这一道题要求的不是求总的个数, 而是要返回一个数组, 记录每一个元素的右边有多少个元素比自己小.所以这里将求逆序对的算法思路并不会详细详解, 如果还不是很了解的宝子们可以先去下面的博客查看:

分治算法(6)_归并排序_交易逆序对的总数-CSDN博客

1. 题目链接

OJ链接 :  计算右侧小于当前元素的个数

2. 题目描述

给你一个整数数组 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]

3. 解法

算法思路:

这一道题的解法与求数组中的逆序对的解法是类似的,但是这⼀道题要求的不是求总的个数,而
是要返回一个数组,记录每一个元素的右边有多少个元素比自己小。
但是在我们归并排序的过程中,元素的下标是会跟着变化的,因此我们需要⼀个辅助数组,来将
组元素和对应的下标绑定在⼀起
归并,也就是再归并元素的时候,顺势将下标也转移到对应的位置
上。
由于我们要快速统计出某⼀个元素后面有多少个比它小的,因此我们可以利用求逆序对的第二种方法。

算法流程:

• 创建两个全局的数组:
    vector<int> index:记录下标
    vector<int> ret:记录结果
    index 用来与原数组中对应位置的元素绑定,ret 用来记录每个位置统计出来的逆序对的个数。

• countSmaller() 主函数:
a.计算 nums 数组大小为 n;
b.初始化定义的两个全局的数组;
    i.为两个数组开辟大小为 n 的空间
    ii.index 初始化为数组下标;
    iii.ret 初始化为 0
c.调用 mergeSort() 函数,并且返回 ret 结果数组。


• void mergeSort(vector<int>&nums, int left, int right) 函数:
函数设计:通过修改全局的数组 ret, 统计出每⼀个位置对应的逆序对的数量,并且排序;
无需返回值,因为直接对全局变量修改,当函数结束的时候,全局变量已经被修改成最后的结果。

• mergeSort() 函数流程:
a.定义递归出口:left >= right 时,直接返回;
b.划分区间:根据中点 mid,将区间划分为[left, mid][mid + 1, right];
c.统计左右两个区间逆序对的数量:
    i.统计左边区间[left, mid] 中每个元素对应的逆序对的数量到 ret 数组中,并排序;
    ii.统计右边区间[mid + 1, right] 中每个元素对应的逆序对的数量到 ret 数组中,并排序。
d.合并左右两个有序区间,并且统计出逆序对的数量:
    i.创建两个大小为 right - left + 1 大小的辅助数组:
        • numsTmp: 排序用的辅助数组;
        • indexTmp:处理下标用的辅助数组。
ii.初始化遍历数组的指针:cur1 = left(遍历左半部分数组)cur2 = mid + 1(遍历右半边数
组)dest = 0(遍历辅助数组)curRet(记录合并时产⽣的逆序对的数量);
iii.循环合并区间:
    • 当 nums[cur1] <= nums[cur2] 时:
        ◦ 说明此时[mid + 1, cur2) 之间的元素都是小于 nums[cur1] 的,需要累加到 ret 数
        组的 indext[cur1] 位置上(因为 index 存储的是元素对应位置在原数组中的下标)
        ◦ 归并排序:不仅要将数据放在对应的位置上,也要将数据对应的坐标也放在对应的位
        置上,使数据与原始的下标绑定在⼀起移动。
    • 当 nums[cur1] > nums[cur2] 时,无需统计,直接归并,注意 index 也要跟着归并。
iv.处理归并排序中剩余的元素;
    • 当左边有剩余的时候,还需要统计逆序对的数量;
    • 当右边还有剩余的时候,无需统计,直接归并。
v.将辅助数组的内容替换到原数组中去;

代码展示:

class Solution 
{
    vector<int> ret;
    vector<int> index;//记录 nums 中当前元素的原始下标
    int tmpnums[100010];
    int tmpindex[100010];

public:
    vector<int> countSmaller(vector<int>& nums) 
    {
        int n = nums.size();
        ret.resize(n);
        index.resize(n);

        //初始化index数组
        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) >> 1;
        //[left, mid] [mid + 1, right]

        //2. 先处理左右两部分
        mergesort(nums, left, mid), mergesort(nums, mid + 1, right);

        //3. 处理一左一右的情况
        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];
        }
    }
};

 

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

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

相关文章

公司防泄密软件哪个好?6款公司内部文件防泄密软件,2024超好用推荐!

企业的核心机密就如同生命之源&#xff0c;然而&#xff0c;数据泄露的风险也随之而来&#xff0c;让不少企业头疼不已。 面对这一挑战&#xff0c;选择一款高效、可靠的防泄密软件显得尤为重要。 那么&#xff0c;公司防泄密软件哪个好&#xff1f; 接下来&#xff0c;就让我…

攻防世界---->[简单] 初识RSA

做题笔记。 下载 是一个.py的文件。 用 Notepad打开瞅瞅。 分析&#xff1a; L (p-1)*(q-1) dgmpy2.invert(e,L) 求逆元快速算出来&#xff1a;invert(e,φ(N)) 求出d值。 n p*q pq p*(q-1) qp q*(p-1) L 【q*(p-1) * p*(q-1)】 // p*q >>> (p-1)*(…

C++关于树的基础知识

首先区分概念 “度为m的树”指的是至少有一个结点的度是m&#xff0c;一定是非空树 “m叉树”指的是允许所有的结点都小于m&#xff0c;且可以是空树 常见考点&#xff1a; 度为m的树的第i层最多有个结点 &#xff08;对于m叉树也相同&#xff09; 第一层m的0次方 第二层m的…

如何帮助企业进行有效的专利管理?

专利管理是企业创新发展的重要支撑&#xff0c;有效的专利管理不仅能保护企业的创新成果&#xff0c;还能提升企业的核心竞争力。那么&#xff0c;究竟该如何帮助企业进行有效的专利管理呢&#xff1f;接下来&#xff0c;本文将为您详细解答。 一、专利管理对企业的重要性 1. 保…

[ROS2]解决PyQt5和sip的各种报错问题 stderr: qt_gui_cpp

前言 编译ros环境的时候遇到了qt_gui_cpp各种编译问题&#xff0c;但是鉴于网上解决方法基本没有&#xff0c;故记录下来帮助后来者。整篇文章总结下来就是一句话&#xff1a;PyQt5和sip安装过程或安装版本有问题&#xff0c;需要重新安装。 问题与解决方法 如果PyQt5你是正…

华为OD机试 - 处理器问题(Python/JS/C/C++ 2024 E卷 200分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…

一文了解:LLM Dropout

咱们来聊聊&#xff0c;啥样的预测模型才算是好模型&#xff1f;简单来说&#xff0c;就是那种在处理它从没见过的数据时&#xff0c;也能表现得特别棒的模型。老派的泛化理论告诉我们&#xff0c;要想让模型在训练集和测试集上都表现差不多&#xff0c;就得让模型简单点。这个…

以一个B站必剪应用Bug过一下CVSS 4.0评分

Bug的内容就是Mac上的必剪无法完成B站视频上传新手任务。 这个从B站客服那里确认了。 没多大事儿,这是个设备相关的bug,我们也可以认为这样的小bug对用户是无害的,此时评分为0,无影响,但从学习角度出发我们将部分评分提高到L,也就是假设这是个CVE的话,聊一下安全问题。…

为什么推荐你一定要弄懂千门八将108局,学会做局思维的人有多么的厉害?

在纷繁复杂的社会与商业环境中&#xff0c;能够洞悉事物本质、预见趋势并巧妙布局的人&#xff0c;往往能在竞争中脱颖而出&#xff0c;成为时代的弄潮儿。而“千门八将108局”这一古老而深邃的智慧体系&#xff0c;不仅蕴含了中国传统文化中对于策略、心理学、人际交往的深刻理…

Java 集合实现类

Java 集合实现类 ​ Java 提供了一套实现了 Collection 接口的标准集合类。其中一些是具体类&#xff0c;这些类可以直接拿来使用&#xff0c;而另外一些是抽象类&#xff0c;提供了接口的部分实现 序号类描述1AbstractCollection 实现了大部分的集合接口。2AbstractList 继承…

java中日期时间类的api

目录 1. Instant 类 构造方法 1&#xff09;Instant.now()&#xff1a;获取当前时间戳。 常用方法 1&#xff09;plus(Duration duration) 2&#xff09;minus(Duration duration) 3&#xff09;toEpochMilli() 4&#xff09;ofEpochMilli(long epochMilli) 5&#xff09;案例:…

网站建设公司哪家好?好的网站建设公司应该有哪些特别之处?

面对众多的网站建设公司&#xff0c;企业该如何选择呢&#xff1f;如何才能避坑呢&#xff1f;本文将探讨好的网站建设公司应该具备的特别之处 案例&#xff0c;这是最直观的表现&#xff0c;一个好的网站建设公司必然拥有为数众多的的案例展示&#xff0c;且这些案例质量高&a…

基于resnet网络【系列】多类别图像识别、迁移学习:猫狗分类实战

目录 1、前言 2、resnet 猫狗分类实战 2.1 训练 2.2 推理 3、更换数据集训练 1、前言 ResNet&#xff08;残差网络&#xff09;是一种深度卷积神经网络架构&#xff0c;广泛用于图像分类任务。它是由微软研究院的研究人员于2015年推出的&#xff0c;以其通过使用残差连接…

Python| 如何使用 DALL·E 和 OpenAI API 生成图像(1)

引言 想象一下&#xff0c;只要描述你想要的画面&#xff0c;电脑就能帮你画出来。这在几年前还像是科幻小说里的场景&#xff0c;但随着神经网络和潜在扩散模型&#xff08;LDM&#xff09;技术的发展&#xff0c;现在已经成为可能。OpenAI 推出的 DALLE 工具&#xff0c;因其…

vue项目刷新后h5样式失效

vue项目刷新后h5样式失效 今天遇到一个bug&#xff0c;有一个Element的message组件&#xff0c;用它做的一个进度条&#xff0c;它是写在一个页面上&#xff0c;并且是用js控制dom元素的 web端一切正常&#xff0c;h5如果从别的页面跳过来也正常&#xff0c;但是&#xff0c;H…

服装生产管理:SpringBoot框架的高效策略

5 系统的实现 5.1 登录界面的实现 用户要想进入本系统必须进行登录操作&#xff0c;进入对应角色登录界面&#xff0c;在登录界面输入系统账号、登录密码&#xff0c;选择登录类型&#xff0c;点击登录按钮进行登录系统&#xff0c;管理员登录界面展示如图5-1所示&#xff0c…

【STM32开发之寄存器版】(八)-定时器的编码器接口模式

一、前言 1.1 编码器接口原理 编码器模式主要用于检测旋转编码器的转动方向和转动速度。旋转编码器一般输出两路相位相差90度的脉冲信号&#xff08;称为A相和B相&#xff09;&#xff0c;通过这两路信号&#xff0c;定时器可以判断编码器的旋转方向&#xff0c;并计数转动的脉…

嵌入式学习-线性表Day03-栈

嵌入式学习-线性表Day03-栈 栈 顺序栈 2&#xff09;入栈 3&#xff09;出栈 链式栈 栈 什么是栈? 只能在一端进行插入和删除操作的线性表&#xff08;又称为堆栈&#xff09;&#xff0c;进行插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底 栈特点&#xff1a; 先进…

儿童(青少年)可以参加哪些含金量高的比赛?

随着素质教育的推进&#xff0c;越来越多的家长和老师开始关注如何培养孩子的综合素质和能力。而参加各类比赛&#xff0c;不仅可以锻炼孩子的思维、动手能力和团队合作精神&#xff0c;还能帮助孩子在学习的过程中找到兴趣点和成就感。尤其是一些含金量高的比赛&#xff0c;不…

什么是静态加载-前端

什么是前端静态加载 在前端开发中&#xff0c;静态加载是一种常见且重要的技术。简单来说&#xff0c;前端静态加载指的是在页面加载时将所需的资源&#xff08;如HTML、CSS、JavaScript、图片等&#xff09;一并加载到用户的浏览器中。这种方式有助于提高页面的加载速度和用户…