算法笔记(五)——分治
文章目录
- 算法笔记(五)——分治
- 快排
- 颜色分类
- 排序数组
- 数组中的第K个最大元素
- 库存管理 III
- 归并
- 排序数组
- 交易逆序对的总数
- 计算右侧小于当前元素的个数
- 翻转对
算法笔记(五)——分治
分治算法字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序)…
分治策略是:对于一个规模为n的问题,若该问题可以容易地解决(比如说规模n较小)则直接解决,否则将其分解为k个规模较小的子问题,这些子问题互相独立且与原问题形式相同,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。这种算法设计策略叫做分治法。
步骤
- 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题
- 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题
- 合并:将各个子问题的解合并为原问题的解
经典的分治算法有二分搜索,归并排序,快速排序,。
快排
颜色分类
题目:颜色分类
思路
- 初始化三个指针:
i
遍历数组;left
左侧均为0
right
右侧均为2
- 遍历过程中遇到
0
,swap(nums[++left],nums[i++])
- 遇到
1
,i++
,不进行交换 - 遇到
2
,swap(nums[--right], nums[i])
- 循环条件
i < right
C++代码
class Solution
{
public:void sortColors(vector<int>& nums) {for(int i = 0, left = -1, right = nums.size(); i < right; ){if(nums[i] == 0) swap(nums[++left], nums[i++]);else if(nums[i] == 1)i++;elseswap(nums[--right], nums[i]);}}
};
排序数组
题目:排序数组
思路
- 我们将数组划分为三块,再来实现快排,将数组划分为三个部分:小于、等于、大于基准值;
<key,=key,>key
三路划分:减少重复元素的递归处理(相同元素过多的话,可以减小递归深度)、避免不必要的交换(将相同元素聚集在一起,避免了不必要的交换操作)
C++代码
class Solution
{
public:int getKey(vector<int>& nums, int left, int right){return nums[rand() % (right - left + 1) + left];}void qsort(vector<int>& nums, int l, int r){if(l >= r) return;int key = getKey(nums, l, r);int i = l, left = l - 1, right = r + 1;while(i < right){if(nums[i] < key) swap(nums[++left], nums[i++]);else if(nums[i] == key) i++;else swap(nums[--right], nums[i]);}qsort(nums, l, left);qsort(nums, right, r);}vector<int> sortArray(vector<int>& nums) {srand(time(NULL));qsort(nums, 0, nums.size() - 1);return nums;}
};
数组中的第K个最大元素
题目:数组中的第K个最大元素
思路
常规解法,利用堆排,但时间复杂度不为O(N)
快速选择算法(快排)
O(N)
- 三路划分,将数组划分为三块;
- 大于
key
的元素个数为c
,等于key
的元素个数为b
,小于key
元素个数为a
- 若
c >= k
,则第k
大元素在右侧,继续在右侧递归寻找第k大元素; - 若
b + c >= k
,则直接返回基准元素,即为第k
大元素; - 若上述均不满足,则第k大元素在左侧,继续在左侧递归寻找第
k
大元素,此时k = k - b - c
C++代码
class Solution
{
public:// 数组中获得随机值 int getKey(vector<int>& nums, int l, int r) {return nums[rand() % (r - l + 1) + l];}int qsort(vector<int>& nums, int l, int r, int k){if(l == r) return nums[l];// 随机选择基准元素int key = getKey(nums, l, r);// 根据基准元素将数组分为三块int i = l, left = l - 1, right = r + 1;while(i < right){if(nums[i] < key) swap(nums[++left], nums[i++]);else if(nums[i] == key) i++;else swap(nums[--right], nums[i]);}int b = right - 1 - (left + 1) + 1; // 等于key的数量int c = r - right + 1; // 大于key的数量if(c >= k) return qsort(nums, right, r, k);else if((b + c) >= k) return key;else return qsort(nums, l, left, k - b - c);}int findKthLargest(vector<int>& nums, int k) {srand(time(NULL));return qsort(nums, 0, nums.size() - 1, k);}
};
库存管理 III
题目:库存管理 III
思路
和上题想法一致,使用快速选择的算法,使时间复杂度达到O(n)
C++代码
```class Solution
{
public:void qsort(vector<int>& nums, int l, int r, int cnt){if(l >= r) return ;int key = nums[rand() % (r - l + 1) + l];int i = l, left = l - 1, right = r + 1;while(i < right){if(nums[i] < key) swap(nums[++left], nums[i++]);else if(nums[i] == key) i++;else swap(nums[--right], nums[i]);}int a = left - l + 1;int b = right - 1 - (left + 1) + 1;if(a >= cnt) qsort(nums, l, left, cnt);else if((a + b) >= cnt) return;else qsort(nums, right, r, cnt - a - b);}vector<int> inventoryManagement(vector<int>& stock, int cnt) {srand(time(NULL));qsort(stock, 0, stock.size() - 1, cnt);return {stock.begin(), stock.begin() + cnt};}
};
归并
排序数组
题目:排序数组
C++代码
class Solution
{// 归并vector<int> tmp;
public:void mergeSort(vector<int>& nums, int l, int r){if(l >= r) return ;// 计算中间位置int mid = (l + r) >> 1;// 对左右两部分进行归并排序mergeSort(nums, l, mid);mergeSort(nums, mid + 1, r);// 归并合并两个有序部分int i = l, j = mid + 1, k = 0;while(i <= mid && j <= r)tmp[k++] = (nums[i] <= nums[j]) ? nums[i++] : nums[j++];while(i <= mid) tmp[k++] = nums[i++];while(j <= r) tmp[k++] = nums[j++];// 拷贝回原数组for(int i = l; i <= r; i++){nums[i] = tmp[i - l];}} vector<int> sortArray(vector<int>& nums) {tmp.resize(nums.size());mergeSort(nums, 0, (int)nums.size() - 1);return nums;}
};
交易逆序对的总数
题目:交易逆序对的总数
思路
当我们将两个已排序的子数组合并成一个有序数组时,如果左侧子数组中的某个元素大于右侧子数组中的某个元素,那么左侧子数组中该元素之后的所有元素(包括该元素本身)都将与右侧子数组中的该元素形成逆序对。因此,我们可以通过计算这样的元素对数来统计逆序对的总数
C++代码
class Solution
{int tmp[50010];
public:int reversePairs(vector<int>& record) {return mergeSort(record, 0, record.size() - 1);}int mergeSort(vector<int>& nums, int left, int right){if(left >= right) return 0; int ret = 0;// 中间,将数组分为两部分int mid = left + right >> 1;// [left, mid], [mid + 1, right]// 左边个数 + 排序 + 右边个数 + 排序ret += mergeSort(nums, left, mid);ret += mergeSort(nums, mid + 1, right);// 一左一右个数int cur1 = left, cur2 = mid + 1, i = 0;while(cur1 <= mid && cur2 <= right){if(nums[cur1] <= nums[cur2]){tmp[i++] = nums[cur1++];}else{ret += mid - cur1 + 1; // 统计逆序对个数tmp[i++] = 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;}
};
计算右侧小于当前元素的个数
题目:计算右侧小于当前元素的个数
思路
这⼀道题的解法与求数组中的逆序对的解法是类似的,记录每⼀个元素的右边有多少个元素⽐⾃⼰⼩
归并排序的过程中,元素的下标是会跟着变化的,因此我们需要⼀个辅助数组,来将数组元素和对应的下标绑定在⼀起归并,也就是再归并元素的时候,顺势将下标也转移到对应的位置上
C++代码
class Solution
{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); index.resize(n);// 初始化tmpIndexfor(int i = 0; i < n; i++) index[i] = i; mergeSort(nums, 0, n - 1);return ret;}void mergeSort(vector<int>& nums, int left, int right){ if(left >= right) return ;// 根据中间元素划分区间int mid = (left + right) >> 1;// [left, mid]、[mid + 1, right]// 处理左右两部分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 = left; j <= right; j++){nums[j] = tmpNums[j - left];index[j] = tmpIndex[j - left];}}
};
翻转对
题目:翻转对
思路
翻转对和逆序对的定义⼤同⼩异,逆序对是前⾯的数要⼤于后⾯的数。⽽翻转对是前⾯的⼀个数要⼤于后⾯某个数的两倍。因此,我们依旧可以⽤归并排序的思想来解决这个问题
C++代码
class Solution
{int tmp[50010];
public:int reversePairs(vector<int>& nums) {return mergeSort(nums, 0, nums.size() - 1);}int mergeSort(vector<int>& nums, int left, int right){if(left >= right) return 0;int ret = 0;int mid = (left + right) >> 1;ret += mergeSort(nums, left, mid);ret += mergeSort(nums, mid + 1, right);int cur1 = left, cur2 = mid + 1, i = left;while(cur1 <= mid) // 降序{while(cur2 <= right && nums[cur2] >= nums[cur1] / 2.0)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[cur2++] : nums[cur1++];while(cur1 <= mid) tmp[i++] = nums[cur1++];while(cur2 <= right) tmp[i++] = nums[cur2++];for(int j = left; j <= right; j++)nums[j] = tmp[j];return ret;}
};
相关文章:

算法笔记(五)——分治
文章目录 算法笔记(五)——分治快排颜色分类排序数组数组中的第K个最大元素库存管理 III 归并排序数组交易逆序对的总数计算右侧小于当前元素的个数翻转对 算法笔记(五)——分治 分治算法字面上的解释是“分而治之”,就…...

多级侧边菜单(递归)
需要编写两个文件 aside-menu.vue 和 menu-item.vue menu-item.vue <script setup> defineOptions({name: MenuItem}) defineProps({menuList: Array}) </script><template><template v-for"menu of menuList"><!-- 如果当前有子菜单&a…...

JavaScript break与continue语句
break语句和continue语句都具有跳转作用,可以让代码不按既有的顺序执行。 break break语句用于跳出代码块或循环 for(i0;i<100;i){if(i5){break;}console.log(i);} continue continue语句用于应即终止本轮循环,返回循环结构的头部,开始下一轮循环。…...
算法【从递归入手一维动态规划】
动态规划:用空间代替重复计算,包含一整套原理和技巧的总和。后面会有非常多的文章介绍动态规划。 有些递归在展开计算时,总是重复调用同一个子问题的解,这种重复调用的递归变成动态规划很有收益。如果每次展开都是不同的解&#…...

Linux中的进程间通信之共享内存
共享内存 共享内存示意图 共享内存数据结构 struct shmid_ds {struct ipc_perm shm_perm; /* operation perms */int shm_segsz; /* size of segment (bytes) */__kernel_time_t shm_atime; /* last attach time */__kernel_time_t shm_dtime; /* last detach time */__kerne…...
第18周 3-过滤器
过滤器(Filter)概念总结 什么是过滤器 过滤器(Filter)是Java Web应用中用于统一拦截和处理请求的组件,类似于现实生活中的空气净化器或安检。它通过对请求进行前置处理,确保请求符合特定要求。 过滤器的…...

Linux之进程概念
作者主页: 作者主页 本篇博客专栏:Linux专栏 创作时间 :2024年9月28日 基本概念: 进程说白了其实就是一个程序的执行实例,正在执行的程序。 在内核层面来说,就是一个担当分配资源(CPU时间…...

小程序-使用npm包
目录 Vant Weapp 安装 Vant 组件库 使用 Vant 组件 定制全局主题样式 API Promise化 1. 基于回调函数的异步 API 的缺点 2. 什么是 API Promise 化 3. 实现 API Promise 化 4.调用 Promise 化之后的异步 API 小程序对 npm 的支持与限制 目前,小程序中已经…...

【springboot】整合沙箱支付
目录 1. 配置沙箱应用环境2. 配置springboot项目1. 引入依赖2. 配置文件注册下载ngrok 3. 创建支付宝支付服务类4. 支付界面模板5. 控制类实现支付6. 测试 1. 配置沙箱应用环境 使用支付宝账号登录到开放平台控制台。 使用支付宝登录后,看到以下页面,下…...

技术速递|Python in Visual Studio Code 2024年9月发布
排版:Alan Wang 我们很高兴地宣布将于 2024 年 9 月发布适用于 Visual Studio Code 的 Python 和 Jupyter 扩展! 此版本包括以下公告: Django 单元测试支持使用 Pylance 从 inlay 提示转到定义 如果您有兴趣,可以在我们的 Pyth…...

数据结构-3.5.队列的顺序实现
一.队列的顺序实现,初始化操作以及判断队列是否为空: 1.图解: 2.代码: #include<stdio.h> #define MaxSize 10 //定义一个队列最多存储的元素个数 typedef struct {int data[MaxSize]; //用静态数组存放队列元素int f…...
preconnect 预解析
preconnect 是一种浏览器优化技术,用于告诉浏览器提前与指定的域名建立连接,包括DNS解析、TCP握手和TLS协商(如果适用)。这样做可以减少客户端在请求资源时所需的往返时间(RTT),从而提高页面加载…...

Leecode热题100-283.移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。 请注意 ,必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1,0,3,12] 输出: [1,3,12,0,0]示例 2: 输入: nums [0] 输出: […...

如何高效使用Prompt与AI大模型对话
一、如何与人工智能对话 在人工智能的世界里,提示词(Prompt)就像是一把钥匙,能够解锁AI智能助手的潜力,帮助你更高效地获取信息、解决问题。但如何正确使用这把钥匙,却是一门艺术。本文将带你了解提示词的…...

Java 之深入理解 String、StringBuilder、StringBuffer
前言 由于发现 String、StringBuilder、StringBuffer 面试的时候会经常问到,这里就顺便总结一下:本文重点会以这三个字符串类的性能、线程安全、存储结构这三个方面进行分析 ✨上期回顾:Java 哈希表 ✨目录 前言 String 介绍 String 的不可变…...

vue3项目执行pnpm update后还原package.json文件后运行报错
项目场景: vue官方版本已更新到vue3.5,项目中还在使用vue3.4,因此想要更新项目vue版本。 问题描述 执行了 pnpm update 命令,一键更新了所有包,更新完成后项目不能正常运行。为了还原项目代码,先删除 nod…...

蓝桥杯【物联网】零基础到国奖之路:十七. 扩展模块之单路ADC和NE555
蓝桥杯【物联网】零基础到国奖之路:十七. 扩展模块之单路ADC和NE555 第一节 硬件解读第二节 CubeMx配置第三节 代码1,脉冲部分代码2,ADC部分代码 第一节 …...
SolveigMM Video Splitter方便快捷视频分割合并软件 V3.6.1309.3-供大家学习研究参考
视频分割功能(Splitter)支持各种编码格式的AVI(DivX、DV、MJPEG、XVID、MPEG-4)、WMV、ASF(DivX、MJPEG、XVID、MPEG-4、WM Video 7/9)F、MPEG(*.mpg、*.mpeg、*.mpv、*.m2v、*.vob)文件、也支持受损的WMV、ASF格式的分割。视频合并功能(Joiner)则支持AVI、WMV/ASF、WMA、MP3、…...

Unity3D 创建一个人物,实现人物的移动
1,创建项目 首先打开我们的Unity Hub 在我们的编译器下面新建项目,选择3D模板,更改一下我们的项目名称,选择一下路径,然后点击创建项目 等待项目创建。。。。。。 我们在项目里先创建一个plane,这样有点视…...

【笔记】数据结构12
文章目录 2013年408应用题41方法一方法二 看到的社区的一个知识总结,这里记录一下。 知识点汇总 2013年408应用题41 解决方法: 方法一 (1)算法思想 算法的策略是从前向后扫描数组元素,标记出一个可能成为主元素的元…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...

Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...

算法打卡第18天
从中序与后序遍历序列构造二叉树 (力扣106题) 给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。 示例 1: 输入:inorder [9,3,15,20,7…...