算法分析—— 《归并排序》

news2025/7/9 3:45:56

《排序数组》

题目描述:

给你一个整数数组 nums,请你将该数组升序排列。

你必须在 不使用任何内置函数 的情况下解决问题,时间复杂度为 O(nlog(n)),并且空间复杂度尽可能小。

示例 1:

输入:nums = [5,2,3,1]
输出:[1,2,3,5]

示例 2:

输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]

代码实现:

class Solution 
{
public:
    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 l, int r)
    {
        if(l >= r) return;

        int mid = (r + l) >> 1;

        MergeSort(nums, l, mid);
        MergeSort(nums, mid + 1, r);

        int cur1 = l, cur2 = mid + 1, i = 0;
        while(cur1 <= mid && cur2 <= r) 
            tmp[i++] = nums[cur1] >= nums[cur2] ? nums[cur2++] : nums[cur1++];
        
        while(cur1 <= mid) tmp[i++] = nums[cur1++];
        while(cur2 <= r) tmp[i++] = nums[cur2++];

        for(int i = l; i <= r; ++i) nums[i] = tmp[i - l];
    }
};

代码解析:

对于同一种排序题目来说,不仅仅要去掌握快速排序,接触其他的排序新思路也尤为重要,不管是归并排序还是快速排序,其本质的思路就是两个字 —— 分治。

所以归并排序是最简单的,利用递归的思路,我们就认为这个递归操作一定可以做到,那么最终就是实现一个「合并两个有序数组」的操作。

那这个操作很简单,利用双指针就可以非常轻松的实现了,在这里我就不过多赘述,因为确实不难。

《交易逆序对的总数》

题目描述:

在股票交易中,如果前一天的股价高于后一天的股价,则可以认为存在一个「交易逆序对」。请设计一个程序,输入一段时间内的股票交易记录 record,返回其中存在的「交易逆序对」总数。

示例 1:

输入:record = [9, 7, 5, 4, 6]
输出:8
解释:交易中的逆序对为 (9, 7), (9, 5), (9, 4), (9, 6), (7, 5), (7, 4), (7, 6), (5, 4)。

代码实现:

class Solution 
{
public:
    vector<int> tmp;
    int reversePairs(vector<int>& record) 
    {
        tmp.resize(record.size());
        return mergesort(record, 0, record.size() - 1);
    }

    int mergesort(vector<int>& record, int left, int right)
    {
        if(left >= right) return 0;

        int mid = (left + right) >> 1;
        
        int ret = 0;

        ret += mergesort(record, left, mid);
        ret += mergesort(record, mid + 1, right);

        int cur1 = left, cur2 = mid + 1, i = 0;

        while(cur1 <= mid && cur2 <= right)
        {
            if(record[cur1] <= record[cur2]) tmp[i++] = record[cur1++];
            else 
            {
                ret += (mid - cur1 + 1);
                tmp[i++] = record[cur2++];
            } 
        }

        while(cur1 <= mid) tmp[i++] = record[cur1++];
        while(cur2 <= right) tmp[i++] = record[cur2++];

        for(int i = left; i <= right; ++i)
        {
            record[i] = tmp[i - left];
        }
        return ret;
    }

};

代码解析:

题目意思很好理解,就是定一个数字,然后去后面找有没有比这个数小的,记住一定是要去后面找。

最简单的解法就是利用双指针套两层for循环直接暴力解决,但是最终会超时,这就很尴尬。

第二个方法,就是利用分治归并的思路来解决题目。

我们最主要的研究可以转换成「找出该数前,有多少个数比我大」

那现在假设我们利用了「归并」将数组排好了升序了,但是还没有「合并两个有序数组」,大致的情况如下:

因为这个判断也需要指针移动,所以我们就可以在排序的过程中,就将答案给算出来。

因为单调性,所以在nums[cur1] > nums[cur2]的情况下,cur1后面的数都大于nums[cur2]

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

题目描述:

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

代码实现:

class Solution 
{
public:
    vector<pair<int, int>> tmp;     // 追踪nums
    vector<int> ret;                // 接受答案
    vector<pair<int, int>> assist;  // 哈希绑定(原数据 + 下标)

    vector<int> countSmaller(vector<int> &nums)
    {
        for (int i = 0; i < nums.size(); ++i)
        {
            assist.push_back(make_pair(nums[i], i));
        }
        ret.resize(nums.size());
        tmp.resize(assist.size());

        Mergesort(assist, 0, nums.size() - 1);
        return ret;
    }

    void Mergesort(vector<pair<int, int>> &assist, int left, int right)
    {
        if (left >= right) return;

        int mid = (right + left) >> 1;

        Mergesort(assist, left, mid);
        Mergesort(assist, mid + 1, right);

        int cur1 = left, cur2 = mid + 1, i = 0;
        while (cur1 <= mid && cur2 <= right)
        {
            if (assist[cur1].first <= assist[cur2].first) tmp[i++] = assist[cur2++];
            else
            {
                ret[assist[cur1].second] += right - cur2 + 1;
                tmp[i++] = assist[cur1++];
            }
        }

        while (cur1 <= mid) tmp[i++] = assist[cur1++];
        while (cur2 <= right) tmp[i++] = assist[cur2++];

        for (int i = left; i <= right; ++i) assist[i] = tmp[i - left];
    }

};

代码解析:

这道题目总体来说,与上一道题的算法思路一样,上一道题是记录有多少个前面大于后面的数字,而这道题是将每个坐标的数字统计起来,然后将确切的数据按照下标统计。

所以使用归并排序,我们无法固定住下标,因为下标会随着排序而发生变化。所以这正是我们需要去解决的。

一开始我考虑使用哈希表,但是数组可能会出现重复数据,所以哈希表pass了,但是我们可以仍然采用哈希的算法思路,将数据和下标绑定在一起。那么这时候我们可以使用vector<pair<int, int>>来模拟哈希表。

拿题目自带的例子:

这样子在排序改变数据位置的同时,下标也随之改变了。

然后也是基于上一道题了,不过这里我们不应该排「升序」,而是排「降序」,因为我们是要找右侧有多少个元素比自己小,那竟然如此我们可以画个图理解理解。

因为单调性,所以nums[cur1]大于cur2后面的全部数。

剩下的思路也是一样的,只是需要注意pair的first和seconde的取值。

《翻转对》

题目描述:

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

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

示例 1:

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

示例 2:

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

注意:

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

代码实现:

class Solution 
{
public:
    vector<int> tmp;
    int reversePairs(vector<int>& nums) 
    {
        tmp.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 mid = (left + right) >> 1;

        int ret = 0;

        ret += MergeSort(nums, left, mid);
        ret += MergeSort(nums, mid + 1, right);

        int cur1 = left, cur2 = mid + 1, i = 0;

        while(cur1 <= mid)
        {
            while(cur2 <= right && nums[cur1] / 2.0 <= nums[cur2]) ++cur2;
            if(cur2 > right) break;

            ret += (right - cur2 + 1);
            ++cur1;
        }

        cur1 = left, cur2 = mid + 1;
        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];

        return ret;
    }

};

代码解析:

一样,这道题和前面的内容算法一样,只不过这里是判断是否大于2倍。

那在这里我们在实现「合并两个有序数组」之前,就可以先通过双指针进行一次判断,再去排序,因为你无法控制是以2倍为标准然后向右移动还是统计数据,而且经过排序后,数据也会乱了,因此我们必须在排序之前就通过双指针做好统计。

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

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

相关文章

SpringBoot启动时报错:cannot use an unresolved DNS server address: I:53

报错如下&#xff1a; 2025-02-17 13:59:41.374 [main] ERROR org.springframework.boot.SpringApplication:835 - Application run failed org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name mySwaggerResourceProvider def…

AI进展不止于基准:深度解析Grok 3的局限

基准测试长期以来一直是AI评估的基石,但任何认真的AI科学家都知道它们是可以被“游戏化”的。 我曾经详细写过这个问题,甚至LMsys也不得不调整其盲测格式——将Grok 3用不同的标签代替,而不仅仅是隐藏品牌——以减少品牌偏见。 高能力AI,尤其是像GPT-4级别的模型,或那些依…

Miniconda + VSCode 的Python环境搭建

目录&#xff1a; 安装 VScode 安装 miniconda 在VScode 使用conda虚拟环境 运行Python程序 1.安装 vscode 编辑器 官网链接&#xff1a;Visual Studio Code - Code Editing. Redefined 下载得到&#xff1a;&#xff0c;双击安装。 安装成功…

防御保护选路练习

拓扑 配置 IP的基本配置 r2 [R2]int g0/0/0 [R2-GigabitEthernet0/0/0]ip add 12.0.0.2 255.255.255.0 [R2]int g0/0/2 [R2-GigabitEthernet0/0/2]ip add 210.1.1.254 255.255.255.0 [R2-GigabitEthernet0/0/2]int g0/0/1 [R2-GigabitEthernet0/0/1]ip add 200.1.1.254 255.…

AI性能极致体验:通过阿里云平台高效调用满血版DeepSeek-R1模型

前言 解决方案链接&#xff1a; https://www.aliyun.com/solution/tech-solution/deepseek-r1-for-platforms?utm_contentg_1000401616 DeepSeek是近期爆火的开源大语言模型&#xff08;LLM&#xff09;&#xff0c;凭借其强大的模型训练和推理能力&#xff0c;受到越来越多…

Windows本地部署DeepSeek

文章目录 一、准备工作1、准备服务器2、准备APP 二、部署deepseek-r11、脚本部署2、脚本部署 三、ChatBox集成 一、准备工作 1、准备服务器 本案例使用Windows电脑 2、准备APP Download Ollama Download Chatbox 二、部署deepseek-r1 1、脚本部署 双击安装完Ollama,默认…

力扣高频sql 50题(基础版) :NULL, 表连接,子查询,case when和avg的结合

NULL的处理 nvl(字段,num) 和数字进行比较需要先使用nvl(字段,num)函数处理空值 思路: 没有被id 2 的客户推荐>> 过滤条件 referee_id !2 没有被id 2 的客户推荐>>被其他客户推荐, 但是也有可能没有被任何客户推荐>>NULL 考点: NULL是 不一个具体的数…

C#中File.Copy方法的参数overwrite取false和true的区别

当调用 System.IO.File.Copy 方法时&#xff0c;第三个参数 overwrite 控制着如果目标位置已经存在同名文件的情况下如何处理。 1、当 overwrite 设置为 true 在这种情况下&#xff0c;即使目标路径下已经有相同名称的文件&#xff0c;该方法也会无条件地覆盖现有的文件。这不…

用promptfoo做大模型安全性测评

1. 引入 promptfoo 是一款专为大模型安全测试打造的强大工具。它能通过红队测试、渗透测试以及漏洞扫描等方式&#xff0c;对各类大模型展开深度安全评估&#xff0c;全面检测模型在不同场景下的安全性。 2. 运行promptfoo的过程 安装nodejs 用npm安装promptfoo npm insta…

用STC-ISP写延时函数

若想写出自己可以定义时长的延时函数&#xff0c;需要重新生成一个1ms的延时函数并稍加修改。 STC-ISP生成的1ms的延时函数代码如下&#xff1a; void Delay1ms(void) //12.000MHz {unsigned char data i, j;i 2;j 239;do{while (--j);} while (--i); }将上述代码改为可自定…

Jetson Agx Orin平台JP6.0-r36.3版本修复了vi模式下的原始图像损坏(线条伪影)

1.问题描述 这是JP-6.0 GA/ l4t-r36.3.0的一个已知问题 通过vi模式捕获的图像会导致异常线条 参考下面的快照来演示这些线伪影 这个问题只能通过VI模式进行修复,不应该通过LibArgus看到。 此外,这是由于内存问题。 由于upstream已经将属性名称更改为“dma-noncoherent”…

MSI微星电脑冲锋坦克Pro Vector GP76 12UGS(MS-17K4)原厂Win11系统恢复镜像,含还原功能,预装OEM系统下载

适用机型&#xff1a;【MS-17K4】 链接&#xff1a;https://pan.baidu.com/s/1P8ZgXc6S_J9DI8RToRd0dQ?pwdqrf1 提取码&#xff1a;qrf1 微星笔记本原装出厂WINDOWS11系统自带所有驱动、出厂主题壁纸、系统属性专属联机支持标志、Office办公软件、MSI Center控制中心等预装…

Pycharm中断点使用技巧

1. 打开项目并准备代码 首先&#xff0c;打开 PyCharm 并加载你的 Python 项目&#xff0c;确保你已经有想要调试的 Python 代码文件。如&#xff1a; def add_numbers(a, b):result a breturn resultnum1 5 num2 3 sum_result add_numbers(num1, num2) print(f"Th…

洛谷P11042 [蓝桥杯 2024 省 Java B] 类斐波那契循环数

像是这种填空题的话&#xff0c;就直接暴力还更加省时间&#xff0c;在本地算完后直接提交答案即可 #include<bits/stdc.h> using namespace std;const int N 10000000;bool isnumber(int n) {vector<int> a;int m n;while (n > 0) {a.push_back(n % 10);n / …

echarts心电图封装方法

效果图 代码 <div id"line1" style"width: 100%;height: 100px;"></div>// 生成图标方法 /*** 生成图表* param {array} cData 图表数据* param {string} home 图表渲染位置Id* param {number} speed 刷新速度 值越大&#xff0c;刷新速度越快…

使用Linux创作第一个小程序--进度条

Linux第一个小程序 - 进度条 储备知识 1.回车换行 回车概念 \r 换行概念 \n 2.缓冲区 sleep 先执行1 后执行2&#xff08;c语言中是按顺序执行的&#xff09; 那么在我sleep期间&#xff0c;“Hello World”一定是被保存起来了&#xff08;缓冲区&#xff09;。 缓冲区&a…

初识LLMs

目录 一、Language AI 历史 二、Language AI如何处理text 三、技术一&#xff1a;Bag-of-Words模型 缺点 四、技术二&#xff1a;word2vec&#xff08;稠密向量 / 嵌入向量&#xff09; 缺点 五、嵌入的多种形式 六、技术三&#xff1a;注意力机制 6.1 上下文嵌入 缺…

SpringAI系列 - RAG篇(三) - ETL

目录 一、引言二、组件说明三、集成示例一、引言 接下来我们介绍ETL框架,该框架对应我们之前提到的阶段1:ETL,主要负责知识的提取和管理。ETL 框架是检索增强生成(RAG)数据处理的核心,其将原始数据源转换为结构化向量并进行存储,确保数据以最佳格式供 AI 模型检索。 …

leetcode:942. 增减字符串匹配(python3解法)

难度&#xff1a;简单 由范围 [0,n] 内所有整数组成的 n 1 个整数的排列序列可以表示为长度为 n 的字符串 s &#xff0c;其中: 如果 perm[i] < perm[i 1] &#xff0c;那么 s[i] I 如果 perm[i] > perm[i 1] &#xff0c;那么 s[i] D 给定一个字符串 s &#xff0…

【智驭未来】使用Deepseek进行业务系统集成场景分析

DeepSeek已经出来了一段时间&#xff0c;各系统厂商纷纷加入对他的支持行列&#xff0c;有使用他来进行数据智能预测分析的&#xff0c;有使用他来进行系统知识智能问答的&#xff0c;有进行多语言处理和文档智能解析的&#xff0c;也有开发工具支持AI代码生成的。根据厂商产品…