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

leetcode - 分治思想

分治 - 快排

这里快排我们统一使用 数组分三块 和 随机产生基准值的方法实现排序

数组分三块:

. - 力扣(LeetCode)

整个思想即将数组按照基准值分为三个区间 , 具体实现: 三指针实现.  遍历指针 , 左区间右边界指针 , 右区间左边界指针

class Solution {//三指针法//格外注意边界值问题//[0,left - 1]  值为0//[left,right]  值为1//[right + 1,nums,length - 1] 值为2public void sortColors(int[] nums) {int left = 0;int right = nums.length - 1;int i = 0;while(i <= right) {if(nums[i] == 0) swap(left ++,i ++,nums);else if(nums[i] == 1) i ++;else swap(right --,i,nums);//注意这里右边交换时i不用++,}}private void swap(int left,int right,int[] nums) {int cur = nums[left];nums[left] = nums[right];nums[right] = cur;}
}

过程

为什么要使用随机数?   基于随机数产生基准值而进行快排时,时间复杂度最为接近O(Logn)

class Solution {public int[] sortArray(int[] nums) {sort(0 , nums.length - 1 , nums);return nums;}private void sort(int left , int right , int[] nums){//递归结束条件if(left >= right) return;int random = new Random().nextInt(right - left + 1);int flag = nums[random + left];//确定基准值int leftFlag = left;int rightFlag = right;int cur = left;while(cur <= rightFlag){if(nums[cur] == flag) cur ++;else if(nums[cur] > flag) swap(nums , cur , rightFlag --);else swap(nums , cur++ , leftFlag ++);}//循环结束时,leftModel - 1为左区间最后一个元素 , leftModel + 1为右区间最后一个元素//递归sort(left , leftFlag - 1 ,nums);sort(rightFlag + 1 , right , nums);}private void swap(int[] nums , int l1 , int l2) {int cur = nums[l1];nums[l1] = nums[l2];nums[l2] = cur;}}

快速选择算法:

. - 力扣(LeetCode)    数组中第k个最大元素  , 要求时间复杂度O(n). 

这道题比较容易想到的解法是 借助堆.    但是题目中要求时间复杂度为On , 堆就不满足要求了.

时间复杂度为O(n). 我们就要想到快速选择排算法

原理:  快排数组分三段的基础上不去对每一段进行排序,而是通过三个区间的元素数量判断目标元素在哪一个区间进行分类讨论:

class Solution {//快速选择算法//基于快排数组分三块的思想:分三块后通过分类确定目标范围private static int dest;public int findKthLargest(int[] nums, int k) {Solution s = new Solution();s.quickChoose(0 , nums.length - 1 , nums , k);return dest;}private void quickChoose(int left , int right , int[] nums , int k){if(right <= left) {dest = nums[left];//这里不要忘记赋值return;}int flag = nums[new Random().nextInt(right - left + 1) + left];System.out.println(left+":" + right+":" +flag);int l = left;int r = right;int cur = left;while(cur <=  r){if(nums[cur] == flag) cur ++;else if(nums[cur] > flag) swap(nums , r -- , cur);//注意右边换过来的是一个新值,cur不进行++else if(nums[cur] < flag) swap(nums , l ++ , cur ++);}//分类讨论 , 根据区间内元素的数量确定下一步计划//1: 目标元素在大于基准值的区间if(right - r >= k) quickChoose(r + 1 , right ,nums , k);//2: 目标元素等于基准值,直接进行返回else if(right - l + 1 >= k) {dest = flag;return;}//3:目标元素在小于基准值的区间else quickChoose(left , l - 1, nums , k - (right - l + 1));}private void swap(int[] nums , int num1 , int num2) {int cur = nums[num1];nums[num1] = nums[num2];nums[num2] = cur;}}

. - 力扣(LeetCode)   库存管理: 选出库存最小的cnt个商品

class Solution {private int str;private int end;//使用快速选择排序public int[] inventoryManagement(int[] stock, int cnt) {quickSort(stock , 0 , stock.length - 1 , cnt);int[] dest = new int[cnt];for(int i = 0;i < cnt;i ++) {dest[i] = stock[i];}return dest;}private void quickSort(int[] nums , int left , int right , int cnt) {if(left >= right) return;int l = left;int r = right;int cur =  left;int flag = nums[new Random().nextInt(right - left + 1) + left];while(cur <= r){if(nums[cur] == flag) cur ++;else if(nums[cur] > flag) swap(r -- , cur , nums);else swap(l ++ , cur++ , nums);}//分类讨论if(l - left > cnt) quickSort(nums , left , l - 1 , cnt);else if(r - left + 1 >= cnt) return;else quickSort(nums , r + 1, right , cnt - (r - left + 1));}private void swap(int num1 , int num2 , int[] nums) {int temp = nums[num1];nums[num1] = nums[num2];nums[num2] = temp;}
}

分治 - 归并

核心思想:   将一个问题分成若干个规模较小的子问题,分别求解后,再将子问题的结果合并,得到原问题的解。归并排序的核心步骤分为两个部分:“分”和“合”。

归并排序

:. - 力扣(LeetCode)

 public int[] sortArray(int[] nums) {sort(nums , 0 , nums.length - 1);return nums;}private void sort(int[] nums , int left , int right) {if(left >= right) return;//不断递归,将数组分割成俩部分,对两部分别再进行分割,直到每一部只剩下一个元素 , 俩俩进行合并int mid = left + (right - left) / 2;sort(nums , left , mid);sort(nums , mid + 1 , right);sortHelper(nums , left , mid , right);//合并的逻辑}//合并的俩个数组分别已经有序 , 建立一个新数组,设置俩个指针对俩个数组进行比较赋值到新数组上,使新数组比较//不要直接再原数组上进行比较交换 , 会打乱数组的有序性 , 将问题复杂化private void sortHelper(int[] nums , int left , int mid , int right) {int l = left;int r = mid + 1;int[] temp = new int[right - left + 1];int cur = 0;while(l <= mid && r <= right) {while(l <= mid && nums[l] <= nums[r]) temp[cur ++] = nums[l ++];while(r <= right && nums[r] <= nums[l]) temp[cur ++] = nums[r ++];}if(l <= mid){while(l <= mid)temp[cur ++] = nums[l ++];}if(r <= right) {while(r <= right) temp[cur ++] = nums[r ++];}for(int i = 0;i < temp.length;i ++) {nums[left + i] = temp[i];}}

优化 : 在进行合并时创建一个全局的,与原数组等大小的临时数组 , 这样就能避免每次进行合并时创建数组的开销.

例题

统计逆序对:  . - 力扣(LeetCode)

在上述归并的逻辑上加上 数组间统计逆序对 的逻辑即可

class Solution {//在进行数组之间合并排序之前加上一个统计数组间逆序队的逻辑`即可public int reversePairs(int[] record) {return sort(record , 0 , record.length - 1);}private int sort(int[] nums  , int left , int right) {if(left >= right) return 0;int mid = left + (right - left) / 2;int num1 = sort(nums , left , mid);int num2 = sort(nums , mid + 1 , right);int num3 = sortHelper(nums , left , mid , right);return (num1 + num2 + num3);}private int sortHelper(int[] nums ,int left ,int mid , int right){int l = left;int r = mid + 1;int count = 0;int[] model = new int[right - left + 1];int cur = 0;while(l <= mid && r <= right){while(l <= mid && nums[l] <= nums[r]) model[cur ++] = nums[l ++];while(r <= right && nums[r] < nums[l]){count += mid - l + 1;//统计逆序对; 因为合并的俩个数组已经分别有序,所以只需要统计数组间即可model[cur ++] = nums[r ++];}}while(l <= mid) model[cur ++] = nums[l ++];while(r <= right) model[cur ++] = nums[r ++];for(int i = 0;i < model.length;i ++) {nums[left + i] = model[i];}return count;}
}

右侧小于自己的数:. - 力扣(LeetCode)

首先:这道题使用暴力求解一定会超时

这道题进行归并的问题是: 归并排序后会进行元素交换, 交换后元素顺序被打乱  , 我们在进行合并数组时统计出来右侧小于当前元素的数后不知道这个元素的原始下标 .  所以这道题相比于上面求逆序对 , 需要多维护一个存储元素下标的数组.  原数组元素顺序改变时 , 这个存储下标的数组也随之进行改变 , 这样,我们就能通过nums中元素的下标拿到当前元素的初始位置 .

class Solution {private int[] indexMoel;//存储每个元素的原始位置 , 随着nums元素交换而交换,nums元素下标对应到indexModel上就是初始元素的位置,通过初始元素位置在dest上进行数量更新private int[] dest;//存储目标元素private int[] numsTemp;//使用全局数组进行合并,避免每次合并创建临时数组的开销private int[] indexTemp;public List<Integer> countSmaller(int[] nums) {indexMoel = new int[nums.length];numsTemp = new int[nums.length];indexTemp = new int[nums.length];dest = new int[nums.length];for(int i = 0;i < nums.length;i ++) {indexMoel[i] = i;}sort(nums , 0 , nums.length - 1);List<Integer> list = new ArrayList<>();for(int i = 0;i < dest.length;i ++) {list.add(dest[i]);}return list;}private void sort(int[] nums , int left , int right) {if(left >= right) return;int mid = left + (right - left) / 2;sort(nums , left , mid);sort(nums , mid + 1 , right);sortHelper(nums , left , mid , right);}private void sortHelper(int[] nums , int left , int mid , int right) {int l = left;int r = mid + 1;int cur1 = 0;int cur2 = 0;//降序排序 , 更好统计while(l <= mid && r <= right) {if(nums[l] > nums[r]) {dest[indexMoel[l]] += right - r + 1;//通过indexmodel拿到当前元素的初始位置进行统计numsTemp[cur1 ++] = nums[l];indexTemp[cur2 ++] = indexMoel[l ++];//indexModel随着nums的交换而交换}else {numsTemp[cur1 ++] = nums[r];indexTemp[cur2 ++] = indexMoel[r ++];}}while(l <= mid){numsTemp[cur1 ++] = nums[l];indexTemp[cur2 ++] = indexMoel[l ++];}while(r <= right){numsTemp[cur1 ++] = nums[r];indexTemp[cur2 ++] = indexMoel[r ++];}for(int i = 0;i < cur1;i ++) {nums[i + left] = numsTemp[i];indexMoel[i + left] = indexTemp[i]; }}
}

相关文章:

leetcode - 分治思想

分治 - 快排 这里快排我们统一使用 数组分三块 和 随机产生基准值的方法实现排序 数组分三块: . - 力扣&#xff08;LeetCode&#xff09; 整个思想即将数组按照基准值分为三个区间 , 具体实现: 三指针实现. 遍历指针 , 左区间右边界指针 , 右区间左边界指针 class Solutio…...

Java面试题·解释题·单例模式、工厂模式、代理模式部分

系列文章目录 Java面试题解释题JavaSE部分 Java面试题解释题框架部分 Java面试题解释题单例模式、工厂模式、代理模式部分 文章目录 系列文章目录前言一、设计模式1. 单例模式1.1 单例模式的定义1.2 单例模式的实现方法 2. 工厂模式2.1 工厂模式的定义2.2 工厂模式的实现方法2…...

如何编写智能合约——基于长安链的Go语言的合约开发

场景设计&#xff1a;文件存证系统 在数字化时代&#xff0c;文件存证和版本追踪变得越来越重要。设想一个场景&#xff1a;在一个法律事务管理系统中&#xff0c;用户需要提交和管理各种文件的版本记录&#xff0c;以确保每个文件在不同时间点的状态可以被准确追踪。文件可能经…...

【PHP代码审计】PHP基础知识

&#x1f31d;博客主页&#xff1a;菜鸟小羊 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 php简介 php是什么&#xff1f; php&#xff08;全称&#xff1a;Hypertext Preprocessor&#xff0c;即超文本预处理器&…...

大模型笔记03--快速体验dify

大模型笔记03--快速体验dify 介绍部署&测试部署 dify测试dify对接本地ollama大模型对接阿里云千问大模型在个人网站中嵌入dify智能客服 注意事项说明 介绍 Dify 是一款开源的大语言模型(LLM) 应用开发平台。它融合了后端即服务&#xff08;Backend as Service&#xff09;…...

Linux常用命令以及操作技巧

&#x1f30f;个人博客主页&#xff1a;意疏-CSDN博客 希望文章能够给到初学的你一些启发&#xff5e; 如果觉得文章对你有帮助的话&#xff0c;点赞 关注 收藏支持一下笔者吧&#xff5e; 阅读指南&#xff1a; 开篇说明帮助命令常见的七个linux操作终端实用的技巧跟文件目录…...

C语言 | Leetcode C语言题解之题409题最长回文串

题目&#xff1a; 题解&#xff1a; int longestPalindrome(char * s) {int c[128]{0},ret0;for(int i0;i<strlen(s);i){c[s[i]];}for(int i0;i<128;i){retc[i]-c[i]%2;}return ret(ret!strlen(s)); }...

FreeSql 全面指南:从基础到高级实战,深入解析读写分离与导航属性

FreeSql 使用详解&#xff1a;从入门到高级 FreeSql 是一个开源的、轻量级的 ORM 框架&#xff0c;它为 .NET 开发人员提供了丰富的功能&#xff0c;包括 CRUD 操作、读写分离、多租户、导航属性支持等。相比于 Entity Framework Core&#xff0c;FreeSql 在性能和特性上有一些…...

深度学习之微积分预备知识点

极限&#xff08;Limit&#xff09; 定义&#xff1a;表示某一点处函数趋近于某一特定值的过程&#xff0c;一般记为 极限是一种变化状态的描述&#xff0c;核心思想是无限靠近而永远不能到达 公式&#xff1a; 表示 x 趋向 a 时 f(x) 的极限。 知识点口诀解释极限的存在左…...

动态内存

动态内存分配函数&#xff1a;在程序运行时为变量或数据结构开辟的内存空间的函数。 有三个重要的动态分配函数&#xff1a;malloc、calloc、realloc。 动态内存分配函数 malloc 这个函数是向内存中申请一块连续的空间&#xff0c;返回一个指向这个块空间的指针。 如果开辟成…...

C/C++实现植物大战僵尸(PVZ)(打地鼠版)

&#x1f680;欢迎互三&#x1f449;&#xff1a;程序猿方梓燚 &#x1f48e;&#x1f48e; &#x1f680;关注博主&#xff0c;后期持续更新系列文章 &#x1f680;如果有错误感谢请大家批评指出&#xff0c;及时修改 &#x1f680;感谢大家点赞&#x1f44d;收藏⭐评论✍ 游戏…...

C++ 科目二 智能指针 [weak_ptr] (解决shared_ptr的循环引用问题)

shared_ptr引入的重复计数问题&#xff0c;导致内存泄漏 using namespace std; class CFather; class CSon;class CFather { public:CFather(){}void Set(shared_ptr<CSon> pson){Pson pson;}shared_ptr<CSon> Pson; };class CSon { public:CSon(){}void Set(sha…...

解决RabbitMQ设置TTL过期后不进入死信队列

解决RabbitMQ设置TTL过期后不进入死信队列 问题发现问题解决方法一&#xff1a;只监听死信队列&#xff0c;在死信队列里面处理业务逻辑方法二&#xff1a;改为自动确认模式 问题发现 最近再学习RabbitMQ过程中&#xff0c;看到关于死信队列内容&#xff1a; 来自队列的消息可…...

【鸿蒙OH-v5.0源码分析之 Linux Kernel 部分】005 - Kernel 入口 C 函数 start_kernel() 源码分析

【鸿蒙OH-v5.0源码分析之 Linux Kernel 部分】005 - Kernel 入口 C 函数 start_kernel 源码分析 系列文章汇总:《鸿蒙OH-v5.0源码分析之 Uboot+Kernel 部分】000 - 文章链接汇总》 本文链接:《【鸿蒙OH-v5.0源码分析之 Linux Kernel 部分】005 - Kernel 入口 C 函数 start_ke…...

EndnoteX9安装及使用教程

EndnoteX9安装及使用教程 一、EndNote安装 1.1 下载 这里提供一个下载链接&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1RlGJksQ67YDIhz4tBmph6Q 提取码&#xff1a;5210 解压完成后&#xff0c;如下所示&#xff1a; 1.2 安装 双击右键进行安装 安装比较简单…...

SQL:子查询

子查询是SQL中强大的功能之一&#xff0c;它允许在一个查询内部嵌套另一个查询&#xff0c;以便处理更复杂的逻辑或数据检索需求。子查询可以用在SELECT、FROM、WHERE、HAVING、IN、ANY、ALL等子句中&#xff0c;根据使用场景和目的的不同&#xff0c;子查询可以分为多种类型。…...

C语言刷题日记(附详解)(5)

一、选填部分 第一题: 下面代码在64位系统下的输出为( ) void print_array(int arr[]) {int n sizeof(arr) / sizeof(arr[0]);for (int i 0; i < n; i)printf("%d", arr[i]); } int main() {int arr[] { 1,2,3,4,5 };print_array(arr);return 0; } A . 1…...

开源加密软件简介

开源加密软件是指源代码公开、可供任何人查看、修改和分发的加密软件。这类软件通常由社区维护&#xff0c;具有高度的透明性和安全性。 1. GnuPG (GNU Privacy Guard) 简介&#xff1a;GnuPG是一种基于OpenPGP标准的加密和签名工具&#xff0c;广泛应用于电子邮件加密和文件…...

【C++学习】 IO 流揭秘:高效数据读写的最佳实践

✨ 今朝有酒今朝醉&#xff0c;明日愁来明日愁 &#x1f30f; &#x1f4c3;个人主页&#xff1a;island1314 &#x1f525;个人专栏&#xff1a;C学习 ⛺️ 欢迎关注&#xff1a;&#x1f44d;点赞 &#x1f442;&#x1f3f…...

C#使用TCP-S7协议读写西门子PLC(五)-测试程序

上面四篇我们进行封装连接PLC以及读写PLC内存地址表 C#使用TCP-S7协议读写西门子PLC(一)-CSDN博客 C#使用TCP-S7协议读写西门子PLC(二)-CSDN博客 C#使用TCP-S7协议读写西门子PLC(三)-CSDN博客 C#使用TCP-S7协议读写西门子PLC(四)-CSDN博客 这里我们进行测试操作 西门子PLC-…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄

文&#xff5c;魏琳华 编&#xff5c;王一粟 一场大会&#xff0c;聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中&#xff0c;汇集了学界、创业公司和大厂等三方的热门选手&#xff0c;关于多模态的集中讨论达到了前所未有的热度。其中&#xff0c;…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

定时器任务——若依源码分析

分析util包下面的工具类schedule utils&#xff1a; ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类&#xff0c;封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz&#xff0c;先构建任务的 JobD…...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块&#xff0c;它提供了一个轻量级的 HTTP 服务器实现&#xff0c;主要用于构建基于 HTTP 的应用程序和服务。 功能介绍&#xff1a; 主要功能 HTTP服务器功能&#xff1a; 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

Psychopy音频的使用

Psychopy音频的使用 本文主要解决以下问题&#xff1a; 指定音频引擎与设备&#xff1b;播放音频文件 本文所使用的环境&#xff1a; Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...