【优选算法】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

前端新手必看:理解并解决‘Failed to fetch‘的完整指南

快速体验 1. 打开 InsCode(快马)平台 https://www.inscode.net 2. 点击'项目生成'按钮,等待项目生成完整后预览效果 输入框内输入如下内容: 创建一个交互式学习模块,包含:1. 动画演示fetch工作原理 2. 常见错误场景可视化 3. 可修改的代码沙盒 4. 逐步修复向导 5. 知识测验。使用纯HTML/CSS/JS实现,适合初学者直接运行学习。 最近在学前端开发时,经常遇到一个让人头疼的错误提示:TypeError: Failed to fetch。刚开始完全摸不着头脑,经过一番摸索后,终于搞清楚了它的来龙去脉。今天就用最直白的语言,分享这个错误的原因和解决方法,希望能帮到同样踩坑的你。 为什么会出现'Failed to

By Ne0inhk
玩转ClaudeCode:使用Figma-MCP编写前端代码1:1还原UI设计图

玩转ClaudeCode:使用Figma-MCP编写前端代码1:1还原UI设计图

目录 本轮目标 具体实践 一、开启 Figma 的 MCP 服务器 二、Claude Code 连接 Figma MCP 三、Claude Code 代码实现 Figma 设计稿 本轮目标 本轮目标是制作数字化大屏的一个前端组件,要求和UI设计图还原度达到1:1。 本轮目标需要我们提前准备好figma客户端,且登录帐号具有开发模式的权限(没有可以去某夕)。Claude Code 就不必多说,没有安装的同学参考我的上一篇文章《玩转ClaudeCode:ClaudeCode安装教程(Windows+Linux+MacOS)》完成安装,通过专属链接注册,可以额外领取100美金的免费使用额度。 安装教程参考:玩转ClaudeCode:ClaudeCode安装教程(Windows+Linux+MacOS)_claude code安装-ZEEKLOG博客文章浏览阅读2.5w次,点赞67次,

By Ne0inhk
Flutter 组件 inappwebview_cookie_manager 适配 鸿蒙Harmony 实战 - 驾驭核心大 Web 容器缓存隧道、构建金融级政企应用绝对防串号跨域大隔离基座

Flutter 组件 inappwebview_cookie_manager 适配 鸿蒙Harmony 实战 - 驾驭核心大 Web 容器缓存隧道、构建金融级政企应用绝对防串号跨域大隔离基座

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 inappwebview_cookie_manager 适配 鸿蒙Harmony 实战 - 驾驭核心大 Web 容器缓存隧道、构建金融级政企应用绝对防串号跨域大隔离基座 前言 在鸿蒙(OpenHarmony)生态的全面爆发元年、特别是涉及到极高密级强网大盾政务信创办公大系统、亦或是动辄流水千万级每日亿级请求的极金融极支付级别的大应用沙盘之中。“如何绝对安全且极其严密地在原生系统底层、Flutter 大前台视图甚至与那些及其脏乱差极不可控的第三方或者内部历史遗留极大旧有 Web H5 容器之间进行身份 Cookie 或者重大利益 Token 的极其安全单向大透传与绝对防死净清理?”这绝对是一扇只要微微由于你的无知或者偷懒漏出那么一丝极小门缝,就会在极短时间导致全应用甚至全行业发生恶性串号、极大账本混乱甚至直接导致极大数据严重越权被全面极光大拉库漏底的核爆级架构黑洞死命门。如果你带领的 0308 批次前端依然只能极其粗糙地开一个极其简陋粗暴且

By Ne0inhk
【前端实战】从 try-catch 回调到链式调用:一种更优雅的 async/await 错误处理方案

【前端实战】从 try-catch 回调到链式调用:一种更优雅的 async/await 错误处理方案

目录 【前端实战】从 try-catch 回调到链式调用:一种更优雅的 async/await 错误处理方案 一、问题背景:async/await 真的解决了一切麻烦吗? 二、真实业务场景下的痛点 1、错误需要“分阶段处理” 2、try-catch 的引入打破了 async/await 的链式范式 三、借鉴 Go、Rust 语言特性,错误也是一种结果 1、错误优先风格替代 try-catch 2、封装一个 safeAsync 工具函数 四、进阶版 safeAsync 函数设计 五、结语         作者:watermelo37         ZEEKLOG优质创作者、华为云云享专家、阿里云专家博主、腾讯云“

By Ne0inhk