优选算法的妙思之流:分治——快排专题

专栏:算法的魔法世界

个人主页:手握风云

目录

一、快速排序

二、例题讲解

2.1. 颜色分类

2.2. 排序数组

2.3. 数组中的第K个最大元素

2.4. 库存管理 III


一、快速排序

        分治,简单理解为“分而治之”,将一个大问题划分为若干个子问题,直到这个子问题能够快速解决。我们之前的快速排序是选出一个数作为基准值,然后将一个数组划分为两个子序列,一个序列<=基准值,另一个>基准值。但这种算法在数据特别大的时候是会超时的。所以我们这里要使用更优秀的三块划分和随机选择基准元素的算法。

二、例题讲解

2.1. 颜色分类

        这道题我们可以参照移动零里面的划分策略。移动零里面是利用双指针将数组分为0区域和非0区域,这道题我们也可以使用三个指针left、right、i来将其划分为0、1、2区域。其中i用来遍历数组,left用来标记0区域的最右侧,right用来标记2区域的最左侧。

        接下来进行分类讨论:如果nums[i]=0,我们让nums[left+1]与nums[i]进行交换,然后i++,left++,就能保证[left+1,i-1]区间还都是1,还可能有一种极端情况,就是i=left+1,自身与自身进行交换,还是得需要left和i++,综上我们就可以写成nums[++left]与nums[i++]进行交换。如果nums[i]=1,我们直接就可以i++就可以。如果nums[i]=2时,right的移动也可以参照上面left的处理,--right,但i不能++,因为i右侧是未遍历的区间,如果i++,就会跳过这个元素。当i=right时,结束循环。

        完整代码实现:

class Solution {public void sortColors(int[] nums) {int left = -1, right = nums.length, i = 0;while (i < right) {if (nums[i] == 0)swap(nums, ++left, i++);else if (nums[i] == 1)i++;else if (nums[i] == 2)swap(nums, --right, i);}}private void swap(int[] nums, int i, int j) {int tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}
}

2.2. 排序数组

        这道题如果我们直接采用之前的快排思想是会超时的,因为如果数组里的元素都等于基准值key,这样数组元素就会跑到数组的最右侧,导致时间复杂度会退化成O(n^{2})

        我们接下来利用数组分三块的思想,将其划分为3个区域,<=key,=key,>=key。这样当基准值都等于key时,时间复杂度直接降为O(n)

        接下来就是如何随机选择基准值。我们需要在数组下标中等概率地选择一个下标,那么我们就可以利用随机数种子,利用公式r%(right-left+1)+left求出随机下标。

        完整代码实现:

class Solution {public int[] sortArray(int[] nums) {Quicksort(nums, 0, nums.length - 1);return nums;}private void Quicksort(int[] nums, int l, int r) {if (l >= r) return;//作为递归结束的条件//数组分三块int key = nums[new Random().nextInt(r - l + 1) + l];int left = l - 1, right = r + 1, i = l;while (i < right) {if (nums[i] < key) swap(nums, ++left, i++);else if (nums[i] == key) i++;else if (nums[i] > key) swap(nums, --right, i);}Quicksort(nums, l, left);Quicksort(nums, right, r);}private void swap(int[] nums, int i, int j) {int tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}
}

2.3. 数组中的第K个最大元素

        因为这道题让我们用时间复杂度为O(n),所以我们的思路很明显要使用快速选择排序,也就是上一题的数组分三块与随机选择基准元素。那么这个第K大的元素就有可能落在三个区域内,我们设三个区域的元素个数分别为a、b、c。如果c>=k,那我们就直接去>key的这个区域去寻找;如果b+c>=k,就直接返回key;如果前两个都不成立,就去<key这个区间去寻找第k-b-c大的元素。

        完整代码实现:

class Solution {public int findKthLargest(int[] nums, int k) {return Quicksort(nums, 0, nums.length - 1, k);}private int Quicksort(int[] nums, int l, int r, int k) {if (l == r) return nums[l];//随机选择基准元素int key = nums[new Random().nextInt(r - l + 1) + l];//根据基准元素,把数组分为三块int left = l - 1, right = r + 1, i = l;while (i < right) {if (nums[i] < key) swap(nums, ++left, i++);else if (nums[i] == key) i++;else if (nums[i] > key) swap(nums, --right, i);}//分类讨论//区间:[l,left],[left+1,right-1],[right,r]int b = right - left - 1, c = r - right + 1;if (c >= k) return Quicksort(nums, right, r, k);else if (b + c >= k) return key;else return Quicksort(nums, l, left, k - b - c);}private void swap(int[] nums, int i, int j) {int tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}
}

2.4. 库存管理 III

        题目就是求数组中的最小的cnt个数。第一种解法,可以使用Arrays.sort()方法来对数组进行排序,找出前k个元素;第二种解法,利用大根堆,创建一个大小为k的大根堆,将数组的前k个元素丢进大根堆中,然后再将数组剩余的元素与堆顶元素比较,如果小就交换并调整堆,最后堆里面就是最小的k个数;第三个解法,就是快速选择算法。第一种解法的时间复杂度为NlogN,第二种解法的时间复杂度为Nlog(cnt),第三中解法的时间复杂度为N

        按照上一题的思路,将数组分为三块,三个区间内元素的个数分别为a、b、c。如果a>cnt,那么我们只需要去<key的区间去寻找;如果a+b>=cnt,此时的cnt一定是大于a的,那么最小的cnt个数,一定位于左侧两个区间,而中间区间又都是等于key的,所以不需要递归,直接++;如果前两个都不成立,直接去最右侧的区间去寻找第cnt-a-b个元素。

        完整代码实现:

class Solution {public int[] inventoryManagement(int[] stock, int cnt) {Quicksort(stock,0,stock.length - 1,cnt);int[] ret = new int[cnt];for (int i = 0; i < cnt; i++) {ret[i] = stock[i];}return ret;}private void Quicksort(int[] nums, int l, int r, int k) {if(l >= r) return;//随机获取基准元素int key = nums[new Random().nextInt(r - l + 1) + l];int left = l - 1,right = r + 1,i = l;//数组分三块while(i < right){if(nums[i] < key) swap(nums,++left,i++);else if (nums[i] == key) i++;else if (nums[i] > key) swap(nums,--right,i);}//分类讨论int a = left - l + 1,b = right - left - 1;if(a > k) Quicksort(nums,l,left,k);else if (a + b >= k) return;else Quicksort(nums,right,r,k - a - b);}private void swap(int[] nums, int i, int j) {int tmp = nums[i];nums[i] = nums[j];nums[j] = tmp;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/75658.shtml

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

相关文章

二叉树的ACM板子(自用)

package 二叉树的中序遍历;import java.util.*;// 定义二叉树节点 class TreeNode {int val; // 节点值TreeNode left; // 左子节点TreeNode right; // 右子节点// 构造函数TreeNode(int x) {val x;} }public class DMain {// 构建二叉树&#xff08;层序遍历方式&…

Linux常用命令详解:从基础到进阶

目录 一、引言 二、文件处理相关命令 &#xff08;一&#xff09;grep指令 &#xff08;二&#xff09;zip/unzip指令 ​编辑 &#xff08;三&#xff09;tar指令 &#xff08;四&#xff09;find指令 三、系统管理相关命令 &#xff08;一&#xff09;shutdown指…

Qt多线程从基础到性能优化

一、为什么需要多线程开发 现代应用程序的性能需求 CPU多核架构的有效利用 复杂任务的解耦与响应式界面保持 二、Qt线程创建四大方式 1. 继承QThread重写run() class WorkerThread : public QThread {void run() override {// 耗时操作qDebug() << "Thread ID…

【java】在 Java 中,获取一个类的`Class`对象有多种方式

在 Java 中&#xff0c;获取一个类的Class对象有多种方式。Class对象代表了 Java 中的一个类或接口的运行时类信息&#xff0c;可以用于反射操作。以下是获取Class对象的几种常见方法&#xff1a; 1.使用.class属性 每个类都有一个.class属性&#xff0c;可以直接获取该类的Cl…

什么是RPC通信

RPC&#xff08;Remote Procedure Call&#xff0c;远程过程调用&#xff09;通信是一种允许程序像调用本地函数一样调用远程服务器上函数的通信技术。它简化了分布式系统中的网络交互&#xff0c;隐藏了底层网络通信的复杂性&#xff0c;使开发者能够专注于业务逻辑。 一、RPC…

还是主题混合程序设计

以下是针对您现有代码的完整主题化改造方案&#xff0c;实现跨QML/Qt Widgets的阴影主题系统&#xff1a; 一、主题管理系统核心 // thememanager.h #pragma once #include <QObject> #include <QColor> #include <QMap> #include <QQmlEngine>class…

BT-Basic函数之首字母T

BT-Basic函数之首字母T 文章目录 BT-Basic函数之首字母Ttabtesttest conttest monitortest on boardstest scanworkstest shortstesthead cleanuptesthead configurationtesthead istesthead power on/offtesthead statustestjet print level istestordertestplan generationth…

7-9 趣味游戏

题目解析 在某个学校的趣味游戏活动中&#xff0c;N 名同学站成一排&#xff0c;他们的年龄恰好是 1 到 N &#xff0c;需要注意的是他们并不是按照年龄的大小排列的&#xff0c;而是随机排列的。 游戏的规则是请同学们快速计算出&#xff0c;如果在这 N 名同学的小组中&…

Hugging Face模型微调训练(基于BERT的中文评价情感分析)

文章目录 学习视频地址项目地址数据集的下载模型微调的基本概念与流程加载数据集数据集格式数据集信息 制作Dataset数据集字段数据集信息 vocab字典操作词汇表文本转换 下游任务模型设计模型训练与保存数据加载优化器训练循环 最终效果评估与测试模型加载和测试 学习视频地址 …

【蓝桥杯】十五届省赛B组c++

目录 前言 握手问题 分析 排列组合写法 枚举 小球反弹 分析 代码 好数 分析 代码 R 格式 分析 代码 宝石组合 分析 代码 数字接龙 分析 代码 拔河 分析 代码 总结 前言 主播这两天做了一套蓝桥杯的省赛题目&#xff08;切实感受到了自己有多菜&#x…

必刷算法100题之计算右侧小于当前元素的个数

题目链接 315. 计算右侧小于当前元素的 个数 - 力扣&#xff08;LeetCode&#xff09; 题目解析 计算数组里面所有元素右侧比它小的数的个数, 并且组成一个数组,进行返回 算法原理 归并解法(分治) 当前元素的后面, 有多少个比我小(降序) 我们要找到第一比左边小的元素, 这…

Hyperlane框架:下一代高性能Rust Web框架 [特殊字符]

Hyperlane框架&#xff1a;下一代高性能Rust Web框架 &#x1f680; 引言 &#x1f44b; 在当今快速发展的Web开发领域&#xff0c;性能和开发效率的平衡变得越来越重要。Hyperlane作为一个新兴的Rust Web框架&#xff0c;完美地解决了这个问题。本文将带您深入了解Hyperlane…

图像处理:使用Numpy和OpenCV实现傅里叶和逆傅里叶变换

文章目录 1、什么是傅里叶变换及其基础理论 1.1 傅里叶变换 1.2 基础理论 2. Numpy 实现傅里叶和逆傅里叶变换 2.1 Numpy 实现傅里叶变换 2.2 实现逆傅里叶变换 2.3 高通滤波示例 3. OpenCV 实现傅里叶变换和逆傅里叶变换及低通滤波示例 3.1 OpenCV 实现傅里叶变换 3.2 实现逆傅…

OpenEuler/CentOS一键部署OpenGauss数据库教程(脚本+视频)

&#x1f4cc;OpenEuler/CentOS一键安装OpenGauss数据库教程 为什么需要OpenGauss一键安装脚本&#xff1f; 手动部署OpenGauss数据库时&#xff0c;环境适配、依赖冲突等问题常让开发者头疼。尤其对新人而言&#xff0c;官方文档的配置步骤可能耗时数小时甚至引发未知报错。 …

如何解决 Hive 在创建 MySQL 表时出现乱码???的问题

1.问题描述 我们启动Hive建立一个学生students表格 使用desc students;查看表格结构时 发现有出现乱码的情况 2.解决方案 打开Hive安装机器上面的MySQL 切换到Hive数据库 执行以下命令修改字段注释字符集 mysql -u root -p123456;use hive;alter table COLUMNS_V2 modify col…

自定义组件触发饿了么表单校验

饿了么的表单控件&#xff0c;如果存在自定义组件更改了值&#xff0c;例如在el-from中存在原生input组件很有可能没法触发表单校验&#xff0c;下拉框或者弹框组件仍然是报红边框。 这是因为饿了么的输入框或者下拉框更改值的时候会自动触发表单校验&#xff0c;但是封装过后的…

架构思维:查询分离 - 表数据量大查询缓慢的优化方案

文章目录 Pre引言案例何谓查询分离&#xff1f;何种场景下使用查询分离&#xff1f;查询分离实现思路1. 如何触发查询分离&#xff1f;方式一&#xff1a; 修改业务代码&#xff1a;在写入常规数据后&#xff0c;同步建立查询数据。方式二&#xff1a;修改业务代码&#xff1a;…

Linux开发工具——make/makefile

&#x1f4dd;前言&#xff1a; 这篇文章我们来讲讲Linux开发工具——make/makefile&#xff1a; &#x1f3ac;个人简介&#xff1a;努力学习ing &#x1f4cb;个人专栏&#xff1a;Linux &#x1f380;CSDN主页 愚润求学 &#x1f304;其他专栏&#xff1a;C学习笔记&#xf…

python加载训练好的模型并进行叶片实例分割预测

要基于“GMT: Guided Mask Transformer for Leaf Instance Segmentation”进行代码复现&#xff0c;可按照以下步骤利用Python实现&#xff1a; 环境配置 克隆仓库&#xff1a;在终端中使用git clone https://github.com/vios-s/gmt-leaf-ins-seg.git命令&#xff0c;将项目代…

AI平台初步规划实现和想法

要实现一个类似Coze的工作流搭建引擎&#xff0c;可以结合SmartEngine作为后端工作流引擎&#xff0c;ReactFlow作为前端流程图渲染工具&#xff0c;以及Ant Design作为UI组件库。以下是实现的步骤和关键点&#xff1a; ### 1. 后端工作流引擎&#xff08;SmartEngine&#xf…