【优选算法】D&C-Mergesort-Harmonies:分治-归并的算法之谐

【优选算法】D&C-Mergesort-Harmonies:分治-归并的算法之谐

文章目录

本篇是优选算法之分治-归并,简单来说就是一个不断分组排序再合并的过程

1.概念解析

🚩什么是分治-归并?

分治归并(基于分治思想的归并排序)是分治算法(Divide and Conquer)在排序问题中的经典应用,核心是通过 “拆分 - 排序 - 合并” 三步,将无序数组转化为有序数组,本质是 “化繁为简、再合简为繁” 的解题思路

2.排序数组

✏️题目描述:

在这里插入图片描述

✏️示例:

在这里插入图片描述
传送门:排序数组

题解:

在这里插入图片描述

本质上分治归并就是一个后序遍历,而快排就是一个前序遍历,不断向下细分数组,然后从下往上把左右两分支的数组排序并合并,以此向上循环往复

💻细节问题:

  • int mid = left + ((right - left) >> 1) 相当于 int mid = left + ((right - left) / 2),二进制的算法效率更高,且该计算中间值的方法能避免整数溢出
  • 最后一步合并数组,nums[left + j] = tmp[j] 而不是 nums[j] = tmp[j],是因为 left 不一定是 0,即不一定是对原来的整个数组进行排序,可能只对数组一部分进行排序
  • 数组排序并不影响逆序对的计算,因为是左右两部分比较,内部已经在递归过程中计算过了

💻代码实现:

#include<iostream>#include<vector>usingnamespace std;classSolution{ vector<int> tmp;public: vector<int>sortArray(vector<int>& nums){ tmp.resize(nums.size());mergeSort(nums,0, nums.size()-1);return nums;}voidmergeSort(vector<int>& nums,int left,int right){if(left >= right){return;}int mid = left +((right - left)>>1);mergeSort(nums, left, mid);mergeSort(nums, mid +1, right);int cur1 = left, cur2 = mid +1, i =0;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 =0; j <= right - left;++j){ nums[left + j]= tmp[j];}}};

3.交易逆序对的总数

✏️题目描述:

在这里插入图片描述

✏️示例:

在这里插入图片描述
传送门:交易逆序对的总数

题解:

在这里插入图片描述

因为归并排序的 “分治 + 有序合并” 特性,完美匹配逆序对统计的核心需求 —— 高效拆分问题、批量计算逆序对,这是暴力枚举做不到的,当 [left,mid][mid+1,right] 进行互相比较时,如果是升序,获取到 record[cur1] >= record[cur2] 时,由于是有序,所以 cur2 往后都是小于 cur1 对应的数的,所以能直接得到很多对逆序数。用降序也是同理

💻代码实现:

classSolution{ vector<int> tmp;public:intreversePairs(vector<int>& record){ tmp.resize(50010);returnmergeSort(record,0, record.size()-1);}intmergeSort(vector<int>&record,int left,int right){if(left >= right){return0;}int ret =0;int mid = left +((right - left)>>1); 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 j =0; j < right - left +1;++j){ record[j + left]= tmp[j];}return ret;}};

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

✏️题目描述:

在这里插入图片描述

✏️示例:

在这里插入图片描述
传送门:计算右侧小于当前元素的个数

题解:

在这里插入图片描述


这题和上一题思路基本一致,唯一的难点就是要额外创建一个数组进行值和下表的绑定,因为题目要求的是返回每个 index 对应的值,有人就问了为什么不能用哈希表,可以是可以但是有重复值的话会很麻烦,因此额外创建一个数组进行 index 和值的绑定更方便,index 数组跟着 nums 数组移动就行

💻代码实现:

classSolution{ vector<int> ret; vector<int> index;int tmpNums[500010];int tmpindex[500010];public: vector<int>countSmaller(vector<int>& nums){int n = nums.size(); ret.resize(n,0); index.resize(n);for(int i =0; i < n;++i){ index[i]= i;}mergeSort(nums,0, n -1);return ret;}voidmergeSort(vector<int>& nums,int left,int right){if(left >= right){return;}int mid = left +((right - left)>>1);mergeSort(nums, left, mid);mergeSort(nums, mid +1, right);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 =0; j < right - left +1;++j){ nums[j + left]= tmpNums[j]; index[j + left]= tmpindex[j];}}};

5.翻转对

✏️题目描述:

在这里插入图片描述

✏️示例:

在这里插入图片描述
传送门:翻转对

题解:

在这里插入图片描述

思路还是利用归并解决,但是要提前计算符合题目要求的翻转对,如果在排序过程中进行计算,会漏掉部分翻转对

💻细节问题:

(long long)nums[cur1] <= 2 * (long long)nums[cur2] 防止溢出

💻代码实现:

classSolution{ vector<int> tmp;int ret =0;public:intreversePairs(vector<int>& nums){ tmp.resize(nums.size());mergeSort(nums,0, nums.size()-1);return ret;}voidmergeSort(vector<int>& nums,int left,int right){if(left >= right){return;}int mid = left +((right - left)>>1);mergeSort(nums, left, mid);zq mergeSort(nums, mid +1, right);int cur1 = left, cur2 = mid +1, i =0;while(cur2 <= right){while(cur1 <= mid &&(longlong)nums[cur1]<=2*(longlong)nums[cur2]){ cur1++;}if(cur1 > mid){break;} ret += mid - cur1 +1; cur2++;} cur1 = left, cur2 = mid +1;while(cur1 <= mid && cur2 <= right){if(nums[cur1]<= nums[cur2]){ tmp[i++]= nums[cur1++];}else{ tmp[i++]= nums[cur2++];}}while(cur1 <= mid){ tmp[i++]= nums[cur1++];}while(cur2 <= right){ tmp[i++]= nums[cur2++];}for(int j =0; j < right - left +1;++j){ nums[j + left]= tmp[j];}}};

希望读者们多多三连支持

小编会继续更新

你们的鼓励就是我前进的动力!

请添加图片描述

Read more

Linux:深入理解网络层

Linux:深入理解网络层

网络层在复杂的网络环境中确定一个合适的路径.传输到指定的网络中 一、网络层的理解 问题1:为什么要有网络层的概念呢?? ——>我们先来讲一个故事:       假设我在学校里被誉为数学大神,是因为我的数学有考满分的能力,但是这种形容只能说明我有很大概率能考满分,而不是说我一定能考满分!!         那我要怎么保证每次都考满分呢??假设我的三叔是学校的教导主任,当我发现这次考试因为粗心没考满分的时候,三叔就会公布此次考试无效,然后让教务处重新出一份试卷再考一次,多考几次那么基本可以保证满分了!!         而此时我是一个具备满分能力的执行者(前提),而三叔是一可以通过重考的决策帮助我百分百考满分的人,所以能力+策略可以完美地完成这件事         此时我就相当于是IP层(跑腿的能力),而三叔就相当于是TCP层(提供可靠性决策)。所以总的来说,用户需要的是一种可以将数据可靠地跨网络从A主机送到B主机的能力,而其中IP协议的本质工作就是提供一种能力,将数据跨网络从A主机送到B主机!!而TCP协议就是提供策略保证这个过程的可靠性。   问题2:数据是如何通

By Ne0inhk
文科生封神!Python+AI 零门槛变现:3 天造 App,指令即收入(附脉脉 AI 沙龙干货)

文科生封神!Python+AI 零门槛变现:3 天造 App,指令即收入(附脉脉 AI 沙龙干货)

🎁个人主页:User_芊芊君子 🎉欢迎大家点赞👍评论📝收藏⭐文章 🔍系列专栏:AI 文章目录: * 一、前言:打破“AI是理科生专属”的迷思 * 二、行业新趋势:为什么文科生学Python+AI更有优势? * 2.1 文科生 vs 理科生:AI时代的核心竞争力对比 * 2.2 核心变现逻辑:靠Python+AI,“指令即收入” * 三、Python+AI零基础学习路径(文科生专属版) * 3.1 学习路径流程图 * 3.2 分阶段学习核心内容(新颖且落地) * 阶段1:Python核心基础(7天)—— 只学“AI开发必备” * 阶段2:AI大模型交互(10天)

By Ne0inhk
【粉丝福利社】构建自主AI深入A2A协议的智能体开发

【粉丝福利社】构建自主AI深入A2A协议的智能体开发

💎【行业认证·权威头衔】 ✔ 华为云天团核心成员:特约编辑/云享专家/开发者专家/产品云测专家 ✔ 开发者社区全满贯:ZEEKLOG博客&商业化双料专家/阿里云签约作者/腾讯云内容共创官/掘金&亚马逊&51CTO顶级博主 ✔ 技术生态共建先锋:横跨鸿蒙、云计算、AI等前沿领域的技术布道者 🏆【荣誉殿堂】 🎖 连续三年蝉联"华为云十佳博主"(2022-2024) 🎖 双冠加冕ZEEKLOG"年度博客之星TOP2"(2022&2023) 🎖 十余个技术社区年度杰出贡献奖得主 📚【知识宝库】 覆盖全栈技术矩阵: ◾ 编程语言:.NET/Java/Python/Go/Node… ◾ 移动生态:HarmonyOS/iOS/Android/小程序 ◾ 前沿领域:

By Ne0inhk
人工智能:循环神经网络(RNN)与序列数据处理实战

人工智能:循环神经网络(RNN)与序列数据处理实战

循环神经网络(RNN)与序列数据处理实战 1.1 本章学习目标与重点 💡 学习目标:掌握循环神经网络的核心原理、经典变体结构,以及在文本序列任务中的实战开发流程。 💡 学习重点:理解 RNN 的循环计算机制,学会使用 TensorFlow/Keras 搭建基础 RNN 与 LSTM 模型,完成文本分类任务。 1.2 循环神经网络核心原理 1.2.1 为什么需要 RNN 💡 传统的前馈神经网络(如 CNN、全连接网络)的输入和输出是相互独立的。它们无法处理序列数据的上下文关联特性。 序列数据在现实中十分常见,比如自然语言文本、语音信号、时间序列数据等。这些数据的核心特点是,当前时刻的信息和之前时刻的信息紧密相关。 循环神经网络通过引入隐藏状态,可以存储历史信息,从而有效捕捉序列数据的上下文依赖关系。 1.2.2 RNN

By Ne0inhk