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

《排序数组》

题目描述:

给你一个整数数组 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;     // 追踪numsvector<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.mzph.cn/web/70114.shtml

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

相关文章

UEFI Spec 学习笔记---11 - Protocols — UEFI Driver Model(1)

11.UEFI Driver Model 遵循 UEFI model 的 EFI driver 是不允许去遍历所有的 controller 来识别需要安装到哪个 controller 上的&#xff0c;而是通过 EFI_BOOT_SERVICES 的 ConnectController 和调用 Binding Driver 来实现&#xff1b; 具体实现如下&#xff1a; CoreConne…

10G EPON光模块

一、10G EPON对称光模块 工作模式&#xff1a;上行突发接收、下行连续发射。 工作原理&#xff1a;当需要发送信号时&#xff0c;系统信号通过光模块的电接口把信号传送到驱动芯片&#xff0c;芯片处理后&#xff0c;驱动激光器发出调制光信号&#xff0c;经光纤发到远端&…

整合SaToken 实现登录功能

整合SaToken 实现登录功能 1.整合redis 1.1添加相关依赖 // 省略...<!-- Redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- Redi…

Vue 项目中逐步引入 TypeScript 的类型检查

在现有的 Vue 项目中逐步引入 TypeScript 的类型检查 本文源于一道面试题&#xff1a;注&#xff1a;两种问法一个意思哈&#xff01;&#xff01; 问题一&#xff1a;“ 老项目Js写的&#xff0c;如何轻量方式享受 ts 类型&#xff1f;” 问题二&#xff1a;“如何 在现有的 …

python后端调用Deep Seek API

python后端调用Deep Seek API 需要依次下载 ●Ollama ●Deepseek R1 LLM模型 ●嵌入模型nomic-embed-text / bge-m3 ●AnythingLLM 参考教程&#xff1a; Deepseek R1打造本地化RAG知识库:安装部署使用详细教程 手把手教你&#xff1a;deepseek R1基于 AnythingLLM API 调用本地…

本地部署MindSearch(开源 AI 搜索引擎框架),然后上传到 hugging face的Spaces——L2G6

部署MindSearch到 hugging face Spaces上——L2G6 任务1 在 官方的MindSearch页面 复制Spaces应用到自己的Spaces下&#xff0c;Space 名称中需要包含 MindSearch 关键词&#xff0c;请在必要的步骤以及成功的对话测试结果当中 实现过程如下&#xff1a; 2.1 MindSearch 简…

matlab下载安装图文教程

【matlab介绍】 MATLAB是一款由美国MathWorks公司开发的专业计算软件&#xff0c;主要应用于数值计算、可视化程序设计、交互式程序设计等高科技计算环境。以下是关于MATLAB的简要介绍&#xff1a; MATLAB是MATrix LABoratory&#xff08;矩阵实验室&#xff09;的缩写&#…

捷米特 JM - RTU - TCP 网关应用 F - net 协议转 Modbus TCP 实现电脑控制流量计

一、项目背景 在某工业生产园区的供水系统中&#xff0c;为了精确监测和控制各个生产环节的用水流量&#xff0c;需要对分布在不同区域的多个流量计进行集中管理。这些流量计原本采用 F - net 协议进行数据传输&#xff0c;但园区的监控系统基于 Modbus TCP 协议进行数据交互&…

4.1 Hugging Face Datasets实战:构建企业级数据流水线

Hugging Face Datasets实战:构建企业级数据流水线 一、Datasets库核心优势 1.1 企业级数据处理需求全景 # 支持的数据格式示例 data_formats = {"结构化数据": ["CSV", "Parquet", "SQL"]

深入解析队列与广度优先搜索(BFS)的算法思想:原理、实现与应用

目录 1. 队列的基本概念 2. 广度优先搜索&#xff08;BFS&#xff09;的基本概念 3. 队列在BFS中的作用 4. BFS的实现细节 5. C实现BFS 6. BFS的应用场景 7. 复杂度分析 8. 总结 1. 队列的基本概念 队列&#xff08;Queue&#xff09;是一种先进先出&#xff08;FIFO, …

【学术投稿-第四届材料工程与应用力学国际学术会议(ICMEAAE 2025】材料工程与应用力学的探讨

重要信息 官网&#xff1a;www.icmeaae.com 时间&#xff1a;2025年3月7-9日 地点&#xff1a;中国西安 简介 第四届材料工程与应用力学&#xff08;ICMEAAE 2025&#xff09;将于2025年3月7日至9日在中国西安召开。本次会议将重点讨论材料科学、应用力学等领域的最新研究进…

间隔连续问题

间隔连续问题 1. 数据结构&#xff1a;某游戏公司记录的用户每日登录数据 表名&#xff1a;game_user 字段名&#xff1a;id&#xff08;用户id&#xff09;、dt&#xff08;日期&#xff09; 2. 需求&#xff1a; ① 创建表 ② 计算每个用户最大的连续登录天数&#xff0c…

EasyRTC轻量级SDK:智能硬件音视频通信资源的高效利用方案

在智能硬件这片广袤天地里&#xff0c;每一份资源的精打细算都关乎产品的生死存亡。随着物联网技术的疾速演进&#xff0c;实时音视频通信功能已成为众多设备的标配。然而&#xff0c;硬件资源的捉襟见肘&#xff0c;让开发者们常常陷入两难境地。EasyRTC&#xff0c;以它的极致…

神经网络剪枝技术的重大突破:sGLP-IB与sTLP-IB

神经网络剪枝技术的重大突破:sGLP-IB与sTLP-IB 在人工智能飞速发展的今天,深度学习技术已经成为推动计算机视觉、自然语言处理等领域的核心力量。然而,随着模型规模的不断膨胀,如何在有限的计算资源和存储条件下高效部署这些复杂的神经网络模型,成为了研究者们亟待解决的…

[AI相关]Unity的C#代码如何简写

是一个某培训机构的飞行棋教学源码 不知道&#xff0c;是否有人想知道怎么可以简写 &#xff08;这个问AI&#xff0c;DeepSeek也应该找不到答案的&#xff09; 静态变量 属性引用 单例 注入 一些UnityEvent特性就不说了。。。 IL 注入 运算符号改写

【Docker】容器被停止/删除的方式及命令:全面解析与实践指南

文章目录 引言一、容器的生命周期二、停止容器的命令及方式1. docker stop 命令2. docker kill 命令3. docker pause 和 docker unpause 命令4. docker restart 命令 三、删除容器的命令及方式1. docker rm 命令2. docker container prune 命令3. docker rm 与 docker rmi 的区…

Node.js技术原理分析系列——Node.js调试能力分析

本文由体验技术团队屈金雄原创。 Node.js 是一个开源的、跨平台的 JavaScript 运行时环境&#xff0c;它允许开发者在服务器端运行 JavaScript 代码。Node.js 是基于 Chrome V8引擎构建的&#xff0c;专为高性能、高并发的网络应用而设计&#xff0c;广泛应用于构建服务器端应…

轻松搭建本地大语言模型(二)Open-WebUI安装与使用

文章目录 前置条件目标一、安装 Open-WebUI使用 Docker 部署 二、使用 Open-WebUI&#xff08;一&#xff09;访问Open-WebUI&#xff08;二&#xff09;注册账号&#xff08;三&#xff09;模型选择&#xff08;四&#xff09;交互 四、常见问题&#xff08;一&#xff09;容器…

阿里云百炼通义大模型

阿里云百炼通义大模型 Part one&#xff08;阿里云百炼大模型&#xff09;一、什么是百炼&#xff08;一&#xff09;调用大模型 二、支持的大模型三、模型总览四、为什么选择百炼&#xff1f;五、开始使用百炼Part two一、开发参考二、模型调用&#xff08;一&#xff09;通义…

Golang学习笔记_33——桥接模式

Golang学习笔记_30——建造者模式 Golang学习笔记_31——原型模式 Golang学习笔记_32——适配器模式 文章目录 桥接模式详解一、桥接模式核心概念1. 定义2. 解决的问题3. 核心角色4. 类图 二、桥接模式的特点三、适用场景1. 多维度变化2. 跨平台开发3. 动态切换实现 四、与其他…