当前位置: 首页 > news >正文

分治归并算法第一弹

目录

前言

1、归并排序

1.1 归并简述

1.2 归并题解

2. 交易中的逆序对

2.1 题目及示例

2.2 解题思路

2.3 题解代码

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

3.1 题目及示例

3.2 解题思路

3.3 题解代码


前言

本文以三道题目为例,讲解了分治归并算法,希望对你有所帮助!


1、归并排序

1.1 归并简述

归并排序(MergeSort)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

归并排序充分体现了分治-归并的思想。

1.2 归并题解

题目链接:https://leetcode.cn/problems/sort-an-array/description/

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

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

示例 1:

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

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

解释:数组排序后,某些数字的位置没有改变(例如,2 和 3),而其他数字的位置发生了改变(例如,1 和 5)。

示例 2:

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

输出:[0,0,1,1,2,5]

解释:请注意,nums 的值不一定唯一。

这里采用递归版本的归并排序

    vector<int> sortArray(vector<int>& nums) {mergeSort(nums, 0, nums.size() - 1);return nums;}void mergeSort(vector<int>& nums, int begin, int end){if (begin >= end)return;// 1.根据中间元素,划分区间int mid = (begin + end) >> 1;// 2.分为两部分[begin, mid] [mid+1, end]mergeSort(nums, begin, mid);mergeSort(nums, mid + 1, end);vector<int> tmp(end - begin + 1);// 3.合并处理int cur1 = begin, cur2 = mid + 1, i = 0;while (cur1 <= mid && cur2 <= end) tmp[i++] = nums[cur1] > nums[cur2] ? nums[cur2++] : nums[cur1++];// 4.处理多余的数while (cur1 <= mid) tmp[i++] = nums[cur1++];while (cur2 <= end) tmp[i++] = nums[cur2++];// 5.还原for (int k = begin; k <= end; k++){nums[k] = tmp[k - begin];}}

2. 交易中的逆序对

2.1 题目及示例

题目链接:https://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-dui-lcof/description/

在股票交易中,如果前一天的股价高于后一天的股价,则可以认为存在一个「交易逆序对」。请设计一个程序,输入一段时间内的股票交易记录 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)。

提示:0 <= record.length <= 50000

2.2 解题思路

我们可以尝试使用分治归并的思想来解题。如下图:

  • 一开始对这个数组进行分解,直到分解成每个子数组中只包含一个元素。对于归并排序来说,此时需要合并。
  • 但是这道题目需要计算逆序对个数,我们可以在两个子数组合并过程中,利用子数组的有序性,选出逆序对。
  • 合并的过程中,每一步都可以挑选出逆序对,只需要将每一步逆序对数量加起来,就是题目的答案。

如下图,我们进一步抽象,将下面的两条线段当做准备合并的两个子数组:

  • cur1和cur2指针执行归并排序中的合并操作。
  • 此时,采用的是升序策略,所以cur1指向的元素左边都是较小元素,右边都是较大元素。
  • 当cur1指向的元素小于等于cur2指向的元素,cur1左边的元素肯定比cur2指向元素更小,形成不了逆序对。cur1右边的元素可能有比cur2指向的元素更大。无法确定逆序对元素个数,所以只执行cur1++的操作。
  • 当cur1指向的元素大于cur2指向的元素,cur1右边的元素肯定比cur2指向的元素更大,所以逆序对个数ret,可以加等上cur1到mid区间的长度,再执行cur2++的操作。

2.3 题解代码

题目中的提示,指出给定数组元素个数区间在[0,50000]。因此,我们可以定义一个固定为50001的整型数组。

class Solution {int tmp[50001];int _n;
public:int reversePairs(vector<int>& record) {_n = record.size();return mergeSort(record, 0, _n - 1);}int mergeSort(vector<int>& nums, int begin, int end){if (begin >= end)return 0;int ret = 0;// 1.根据中间元素,划分区间int mid = (begin + end) >> 1;// [begin, mid] [mid+1, end]// 2.先处理左右两部分ret += mergeSort(nums, begin, mid);ret += mergeSort(nums, mid + 1, end);// 3.处理一左一右的情况int cur1 = begin, cur2 = mid + 1, i = 0;while (cur1 <= mid && cur2 <= end) {if (nums[cur1] <= nums[cur2]) {tmp[i++] = nums[cur1++];} else {ret += mid - cur1 + 1;tmp[i++] = nums[cur2++];}}// 4.处理剩下的排序过程while (cur1 <= mid) {tmp[i++] = nums[cur1++];}while (cur2 <= end) {tmp[i++] = nums[cur2++];}// 还原for (int k = begin; k <= end; k++){nums[k] = tmp[k - begin];}return ret;}
};

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

3.1 题目及示例

题目链接:https://leetcode.cn/problems/count-of-smaller-numbers-after-self/description/

给你一个整数数组 nums ,按要求返回一个新数组 counts 。数组 counts 有该性质: counts[i] 的值是  nums[i] 右侧小于 nums[i] 的元素的数量。

提示:1 <= nums.length <= 105    -104 <= nums[i] <= 104

示例 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.2 解题思路

这道题目求解的是每个元素对应的逆序对个数,不是逆序对总数,并且要求最后按照原来下标存放每个元素元素的逆序对个数。

  • 合并排序的过程中,我们会使用新数组存放合并排序的结果,再把该结果拷贝会原数组。
  • 因为我们得清楚每个元素的初始下标,所以我们还需要定义一个tmpIndex,tmpIndex[i]就表示合并后,在i下标的元素的初始下标值。并且一开始要对tmpIndex初始化,使得tmpIndex[i] = i
  • 所以,每次合并排序需要更新两个数组。

这次是降序排序,使用的策略是找出i位置元素后面比nums[i]的元素个数。分析思路跟上题类似。

  • 当nums[cur1]小于等于nums[cur2],cur2左边的元素肯定比cur1所在元素大,而cur2右侧的元素不能确定是否比cur1所在元素的大小,所以更新tmpIndex和tmpNum两个数组。
  • 当nums[cur1]大于等于nums[cur2],cur2右边的元素肯定小于cur1所在的元素。创建一个ret数组,在index[cur1]]下标下,加上end - cur2 + 1的数值。继续更新tmpIndex和tmpNum两个数组。

3.3 题解代码

题目中的提示,指出给定数组元素个数区间在[0,200000]。因此,我们可以定义两个固定为200001的整型数组,分表是tmpNum和tmpIndex。

class Solution {vector<int> ret;vector<int> index;int tmp[200000];int tmpIndex[200000];int _n;
public:vector<int> countSmaller(vector<int>& nums) {_n = nums.size();ret.resize(_n, 0);index.resize(_n, 0);for (int i = 1; i < _n; i++){index[i] = i;}mergeSort(nums, 0, _n - 1);return ret;}void mergeSort(vector<int>& nums, int begin, int end){if (begin >= end)return;// 1.根据中间元素,划分区间int mid = (begin + end) >> 1;// [begin, mid] [mid+1, end]// 2.先处理左右两部分mergeSort(nums, begin, mid);mergeSort(nums, mid + 1, end);// 3.处理一左一右的情况int cur1 = begin, cur2 = mid + 1, i = 0;while (cur1 <= mid && cur2 <= end) {if (nums[cur1] <= nums[cur2]) {tmpIndex[i] = index[cur2]; tmp[i++] = nums[cur2++];} else {ret[index[cur1]] += end - cur2 + 1;tmpIndex[i] = index[cur1];tmp[i++] = nums[cur1++];}}// 4.处理剩下的排序过程while (cur1 <= mid) {tmpIndex[i] = index[cur1];tmp[i++] = nums[cur1++];}while (cur2 <= end) {tmpIndex[i] = index[cur2]; tmp[i++] = nums[cur2++];}// 还原for (int k = begin; k <= end; k++){nums[k] = tmp[k - begin];index[k] = tmpIndex[k - begin];}}
};


创作充满挑战,但若我的文章能为你带来一丝启发或帮助,那便是我最大的荣幸。如果你喜欢这篇文章,请不吝点赞、评论和分享,你的支持是我继续创作的最大动力!

http://www.dtcms.com/a/589156.html

相关文章:

  • 【数据结构】哈夫曼树技术详解:原理、算法与应用
  • 贵阳网站备案在哪里网站红色
  • 个人网站公司网站区别经营区别数字货币怎么推广赚钱
  • 3GPP 各主要 Release(版本)及其发布时间和主要内容
  • RK3588同时硬解和GPU绘制16路1080P/通用其他RK系列板子/嵌入式监控系统/支持国产硬件和系统
  • BB ACS355变频器家装EMC电源滤波安装与使用指南
  • ARMV9.7 FEAT_SME2p3 视频编解码器新增指令扩展
  • 基础开发工具(下)
  • 一文详解分布式事务
  • HarmonyOS DataShareExtension深度解析:构建安全高效的数据共享架构
  • 团风做网站网站建站前期准备工作
  • .net 网站开发架构企业网站设置
  • 面试题 16.25. LRU 缓存
  • st表详解
  • 企业网站优化甲薇g71679做同等效果下拉词做网站白云
  • 9、webgl 基本概念 + 复合变换 + 平面内容复习
  • gRPC C++库架构与异步编程实践
  • 做网站如何收益发送wordpress
  • 人工智能备考——4部分总结
  • Vite.js 快速入门指南 (React + JavaScript 版)
  • 如何建微信商城网站wordpress手机模板
  • 基于springboot纺织品企业财务管理系统【带源码和文档】
  • CHAR、VARCHAR、TEXT 的差别与存储方式
  • QtMainWindow C++详解:构建桌面应用的核心框架
  • 红帽虚拟机,NG搭建网站练习
  • EntryAbility继承FlutterAbility应用入口深度解析
  • (3)项目启航:Qt实战项目之创建项目
  • 补充说明:Windows 完全可以开发 Qt 鸿蒙应用!(附专属适配方案)
  • Apache 工具包(commons-io commons-lang3 )保姆介绍
  • 大小鼠跑步机 小动物跑台 动物跑步机 大鼠实验跑台