【leetcode练习·二叉树拓展】快速排序详解及应用
本文参考labuladong算法笔记[拓展:快速排序详解及应用 | labuladong 的算法笔记]
1、算法思路
首先我们看一下快速排序的代码框架:
def sort(nums: List[int], lo: int, hi: int):if lo >= hi:return# 对 nums[lo..hi] 进行切分# 使得 nums[lo..p-1] <= nums[p] < nums[p+1..hi]p = partition(nums, lo, hi)# 去左右子数组进行切分sort(nums, lo, p - 1)sort(nums, p + 1, hi)
其实你对比之后可以发现,快速排序就是一个二叉树的前序遍历:
# 二叉树遍历框架
def traverse(root: TreeNode):if not root:return# 前序位置print(root.val)traverse(root.left)traverse(root.right)
另外,前文 归并排序详解 用一句话总结了归并排序:先把左半边数组排好序,再把右半边数组排好序,然后把两半数组合并。
同时我提了一个问题,让你一句话总结快速排序,这里说一下我的答案:
快速排序是先将一个元素排好序,然后再将剩下的元素排好序。
为什么这么说呢,且听我慢慢道来。
快速排序的核心无疑是 partition
函数, partition
函数的作用是在 nums[lo..hi]
中寻找一个切分点 p
,通过交换元素使得 nums[lo..p-1]
都小于等于 nums[p]
,且 nums[p+1..hi]
都大于 nums[p]
:
一个元素左边的元素都比它小,右边的元素都比它大,啥意思?不就是它自己已经被放到正确的位置上了吗?
所以 partition
函数干的事情,其实就是把 nums[p]
这个元素排好序了。
一个元素被排好序了,然后呢?你再把剩下的元素排好序不就得了。
剩下的元素有哪些?左边一坨,右边一坨,去吧,对子数组进行递归,用 partition
函数把剩下的元素也排好序。
从二叉树的视角,我们可以把子数组 nums[lo..hi]
理解成二叉树节点上的值,sort
函数理解成二叉树的遍历函数。
参照二叉树的前序遍历顺序,快速排序的运行过程如下 GIF:
你注意最后形成的这棵二叉树是什么?是一棵二叉搜索树:
这应该不难理解吧,因为 partition
函数每次都将数组切分成左小右大两部分,恰好和二叉搜索树左小右大的特性吻合。
你甚至可以这样理解:快速排序的过程是一个构造二叉搜索树的过程。
但谈到二叉搜索树的构造,那就不得不说二叉搜索树不平衡的极端情况,极端情况下二叉搜索树会退化成一个链表,导致操作效率大幅降低。
快速排序的过程中也有类似的情况,比如我画的图中每次 partition
函数选出的切分点都能把 nums[lo..hi]
平分成两半,但现实中你不见得运气这么好。
如果你每次运气都特别背,有一边的元素特别少的话,这样会导致二叉树生长不平衡:
这样的话,时间复杂度会大幅上升,后面分析时间复杂度的时候再细说。
我们为了避免出现这种极端情况,需要引入随机性。
常见的方式是在进行排序之前对整个数组执行 洗牌算法 进行打乱,或者在 partition
函数中随机选择数组元素作为切分点,本文会使用前者。
2、代码实现
import randomclass Quick:@staticmethoddef sort(nums: List[int]):# 为了避免出现耗时的极端情况,先随机打乱random.shuffle(nums)# 排序整个数组(原地修改)Quick.sort_(nums, 0, len(nums) - 1)@staticmethoddef sort_(nums: List[int], lo: int, hi: int):if lo >= hi:return# 对 nums[lo..hi] 进行切分# 使得 nums[lo..p-1] <= nums[p] < nums[p+1..hi]p = Quick.partition(nums, lo, hi)Quick.sort_(nums, lo, p - 1)Quick.sort_(nums, p + 1, hi)# 对 nums[lo..hi] 进行切分@staticmethoddef partition(nums: List[int], lo: int, hi: int) -> int:pivot = nums[lo]# 关于区间的边界控制需格外小心,稍有不慎就会出错# 我这里把 i, j 定义为开区间,同时定义:# [lo, i) <= pivot;(j, hi] > pivot# 之后都要正确维护这个边界区间的定义i, j = lo + 1, hi# 当 i > j 时结束循环,以保证区间 [lo, hi] 都被覆盖while i <= j:while i < hi and nums[i] <= pivot:i += 1# 此 while 结束时恰好 nums[i] > pivotwhile j > lo and nums[j] > pivot:j -= 1# 此 while 结束时恰好 nums[j] <= pivotif i >= j:break# 此时 [lo, i) <= pivot && (j, hi] > pivot# 交换 nums[j] 和 nums[i]nums[i], nums[j] = nums[j], nums[i]# 此时 [lo, i] <= pivot && [j, hi] > pivot# 最后将 pivot 放到合适的位置,即 pivot 左边元素较小,右边元素较大nums[lo], nums[j] = nums[j], nums[lo]return j
上面代码里partition采用的是左右双指针法,也可用快慢双指针,更易理解:
选最后一个元素作为分区点,指针 i 表示比分区值小的元素应该放的位置,指针 j 只用来遍历。当 j 遍历到比分区值小的元素时,放到指针 i 的位置(通过交换实现)。当 j 遍历完时,[lo, i - 1] 都是比分区值小的元素,[i, hi - 1] 都是比分区值大的元素,最后交换一下分区值和 i 所指向的元素便实现了 pivot 左边都是比它小的元素,右边都是比它大的元素。
# 快慢双指针def partition(nums, lo, hi):pivot = nums[hi]i = j = lowhile j < hi:if nums[j] < pivot:nums[i], nums[j] = nums[j], nums[i]i += 1j += 1nums[i], nums[hi] = nums[hi], nums[i]return i
想要正确寻找切分点非常考验你对边界条件的控制,稍有差错就会产生错误的结果。
处理边界细节的一个技巧就是,你要明确每个变量的定义以及区间的开闭情况。具体的细节看代码注释,建议自己动手实践。
3、复杂度分析
接下来分析一下快速排序的时间复杂度。
显然,快速排序的时间复杂度主要消耗在 partition
函数上,因为这个函数中存在循环。
所以 partition
函数到底执行了多少次?每次执行的时间复杂度是多少?总的时间复杂度是多少?
和归并排序类似,需要结合之前画的这幅图来从整体上分析:
partition
执行的次数是二叉树节点的个数,每次执行的复杂度就是每个节点代表的子数组 nums[lo..hi]
的长度,所以总的时间复杂度就是整棵树中「数组元素」的个数。
假设数组元素个数为 N
,那么二叉树每一层的元素个数之和就是 O(N)O(N);切分点 p
每次都落在数组正中间的理想情况下,树的层数为 O(logN)O(logN),所以理想的总时间复杂度为 O(NlogN)O(NlogN)。
由于快速排序没有使用任何辅助数组,所以空间复杂度就是递归堆栈的深度,也就是树高 O(logN)O(logN)。
当然,我们之前说过快速排序的效率存在一定随机性,如果每次 partition
切分的结果都极不均匀:
快速排序就退化成选择排序了,树高为 O(N)O(N),每层节点的元素个数从 N
开始递减,总的时间复杂度为:
N + (N - 1) + (N - 2) + ... + 1 = O(N^2)
所以我们说,快速排序理想情况的时间复杂度是 O(NlogN)O(NlogN),空间复杂度 O(logN)O(logN),极端情况下的最坏时间复杂度是 O(N2)O(N2),空间复杂度是 O(N)O(N)。
不过大家放心,经过随机化的 partition
函数很难出现极端情况,所以快速排序的效率还是非常高的。
还有一点需要注意的是,快速排序是「不稳定排序」,与之相对的,前文讲的 归并排序 是「稳定排序」。
对于序列中的相同元素,如果排序之后它们的相对位置没有发生改变,则称该排序算法为「稳定排序」,反之则为「不稳定排序」。
如果单单排序 int 数组,那么稳定性没有什么意义。但如果排序一些结构比较复杂的数据,那么稳定排序就有更大的优势了。
比如说你有若干订单数据,已经按照订单号排好序了,现在你想对订单的交易日期再进行排序:
如果用稳定排序算法(比如归并排序),那么这些订单不仅按照交易日期排好了序,而且相同交易日期的订单的订单号依然是有序的。
但如果你用不稳定排序算法(比如快速排序),那么虽然排序结果会按照交易日期排好序,但相同交易日期的订单的订单号会丧失有序性。
在实际工程中我们经常会将一个复杂对象的某一个字段作为排序的 key
,所以应该关注编程语言提供的 API 底层使用的到底是什么排序算法,是稳定的还是不稳定的,这很可能影响到代码执行的效率甚至正确性。
912. 排序数组
给你一个整数数组 nums
,请你将该数组升序排列。
你必须在 不使用任何内置函数 的情况下解决问题,时间复杂度为 O(nlog(n))
,并且空间复杂度尽可能小。
示例 1:
输入:nums = [5,2,3,1] 输出:[1,2,3,5]
示例 2:
输入:nums = [5,1,1,2,0,0] 输出:[0,0,1,1,2,5]
提示:
1 <= nums.length <= 5 * 104
-5 * 104 <= nums[i] <= 5 * 104
class Solution:def sortArray(self, nums: List[int]) -> List[int]:# 归并排序对数组进行原地排序Quick.sort(nums)return numsclass Quick:# 见上文
以上代码重点在于对快速排序代码框架的理解,但遇到极端情况还是会超时,下面是通常的快排算法代码:
class Solution:def sortArray(self, nums: List[int]) -> List[int]:def partition(arr, low, high):# 随机选择pivotpivot_idx = random.randint(low, high) # pivot放置到最左边 arr[low], arr[pivot_idx] = arr[pivot_idx], arr[low] # 选取最左边为pivot pivot = arr[low] left, right = low, high # 双指针while left < right:# 找到右边第一个<pivot的元素while left < right and arr[right] >= pivot: right -= 1# 并将其移动到left处arr[left] = arr[right] # 找到左边第一个>pivot的元素while left < right and arr[left] <= pivot: left += 1# 并将其移动到right处arr[right] = arr[left] # pivot放置到中间left=right处arr[left] = pivot return leftdef quick_sort(arr, low, high):if low >= high: # 递归结束return mid = partition(arr, low, high) # 以mid为分割点quick_sort(arr, low, mid-1) # 递归对mid两侧元素进行排序quick_sort(arr, mid+1, high)quick_sort(nums, 0, len(nums)-1) # 调用快排函数对nums进行排序return nums
4、快速选择算法
不仅快速排序算法本身很有意思,而且它还有一些有趣的变体,最有名的就是快速选择算法(Quick Select)。
215. 数组中的第K个最大元素
给定整数数组 nums
和整数 k
,请返回数组中第 k
个最大的元素。
请注意,你需要找的是数组排序后的第 k
个最大的元素,而不是第 k
个不同的元素。
你必须设计并实现时间复杂度为 O(n)
的算法解决此问题。
示例 1:
输入: [3,2,1,5,6,4],
k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6],
k = 4
输出: 4
提示:
1 <= k <= nums.length <= 105
-104 <= nums[i] <= 104
题目要求我们寻找第 k
个最大的元素,稍微有点绕,意思是去寻找 nums
数组降序排列后排名第 k
的那个元素。
比如输入 nums = [2,1,5,4], k = 2
,算法应该返回 4,因为 4 是 nums
中第 2 个最大的元素。
快速选择算法是快速排序的变体,效率更高,面试中如果能够写出快速选择算法,肯定是加分项。
首先,题目问「第 k
个最大的元素」,相当于数组升序排序后「排名第 n - k
的元素」,为了方便表述,后文另 k' = n - k
。
如何知道「排名第 k'
的元素」呢?其实在快速排序算法 partition
函数执行的过程中就可以略见一二。
我们刚说了,partition
函数会将 nums[p]
排到正确的位置,使得 nums[lo..p-1] < nums[p] < nums[p+1..hi]
:
这时候,虽然还没有把整个数组排好序,但我们已经让 nums[p]
左边的元素都比 nums[p]
小了,也就知道 nums[p]
的排名了。
那么我们可以把 p
和 k'
进行比较,如果 p < k'
说明第 k'
大的元素在 nums[p+1..hi]
中,如果 p > k'
说明第 k'
大的元素在 nums[lo..p-1]
中。
进一步,去 nums[p+1..hi]
或者 nums[lo..p-1]
这两个子数组中执行 partition
函数,就可以进一步缩小排在第 k'
的元素的范围,最终找到目标元素。
这样就可以写出解法代码:
import randomclass Solution:def findKthLargest(self, nums: List[int], k: int) -> int:# 首先随机打乱数组random.shuffle(nums)lo, hi = 0, len(nums) - 1# 转化成「排名第 k 的元素」k = len(nums) - kwhile lo <= hi:# 在 nums[lo..hi] 中选一个切分点p = self.partition(nums, lo, hi)if p < k:# 第 k 大的元素在 nums[p+1..hi] 中lo = p + 1elif p > k:# 第 k 大的元素在 nums[lo..p-1] 中hi = p - 1else:# 找到第 k 大元素return nums[p]return -1# 对 nums[lo..hi] 进行切分def partition(self, nums: List[int], lo: int, hi: int) -> int:# 见前文pass
这个代码框架其实非常像我们前文 二分搜索框架 的代码,这也是这个算法高效的原因,但是时间复杂度为什么是 O(N) 呢?
显然,这个算法的时间复杂度也主要集中在 partition
函数上,我们需要估算 partition
函数执行了多少次,每次执行的时间复杂度是多少。
最好情况下,每次 partition
函数切分出的 p
都恰好是正中间索引 (lo + hi) / 2
(二分),且每次切分之后会到左边或者右边的子数组继续进行切分,那么 partition
函数执行的次数是 logN,每次输入的数组大小缩短一半。
所以总的时间复杂度为:
// 等比数列
N + N/2 + N/4 + N/8 + ... + 1 = 2N = O(N)
当然,类似快速排序,快速选择算法中的 partition
函数也可能出现极端情况,最坏情况下 p
一直都是 lo + 1
或者一直都是 hi - 1
,这样的话时间复杂度就退化为 O(N^2)了:
N + (N - 1) + (N - 2) + ... + 1 = O(N^2)
这也是我们在代码中使用 shuffle
函数的原因,通过引入随机性来避免极端情况的出现,让算法的效率保持在比较高的水平。随机化之后的快速选择算法的复杂度可以认为是 O(N)。
其他解法:
class Solution:def findKthLargest(self, nums, k):def quick_select(nums, k):# 随机选择基准数pivot = random.choice(nums)big, equal, small = [], [], []# 将大于、小于、等于 pivot 的元素划分至 big, small, equal 中for num in nums:if num > pivot:big.append(num)elif num < pivot:small.append(num)else:equal.append(num)if k <= len(big):# 第 k 大元素在 big 中,递归划分return quick_select(big, k)if len(big) + len(equal) < k:# 第 k 大元素在 small 中,递归划分return quick_select(small, k - len(nums) + len(small))# 第 k 大元素在 equal 中,直接返回 pivotreturn pivotreturn quick_select(nums, k)
到这里,快速排序算法和快速选择算法就讲完了,从二叉树的视角来理解思路应该是不难的,但 partition
函数对细节的把控需要你多花心思去理解和记忆。
最后你可以比较一下快速排序和前文讲的 归并排序 并且可以说说你的理解:为什么快速排序是不稳定排序,而归并排序是稳定排序?
相关文章:
【leetcode练习·二叉树拓展】快速排序详解及应用
本文参考labuladong算法笔记[拓展:快速排序详解及应用 | labuladong 的算法笔记] 1、算法思路 首先我们看一下快速排序的代码框架: def sort(nums: List[int], lo: int, hi: int):if lo > hi:return# 对 nums[lo..hi] 进行切分# 使得 nums[lo..p-1]…...
Gurobi基础语法之 addConstr, addConstrs, addQConstr, addMQConstr
在新版本的 Gurobi 中,向 addConstr 这个方法中传入一个 TempConstr 对象,在模型中就会根据这个对象生成一个约束。更重要的是:TempConstr 对象可以传给所有addConstr系列方法,所以下面先介绍 TempConstr 对象 TempConstr TempC…...
游戏引擎 Unity - Unity 设置为简体中文、Unity 创建项目
Unity Unity 首次发布于 2005 年,属于 Unity Technologies Unity 使用的开发技术有:C# Unity 的适用平台:PC、主机、移动设备、VR / AR、Web 等 Unity 的适用领域:开发中等画质中小型项目 Unity 适合初学者或需要快速上手的开…...
Kamailio、MySQL、Redis、Gin后端、Vue.js前端等基于容器化部署
基于容器化的部署方案,通常会将每个核心服务(如Kamailio、MySQL、Redis、Gin后端、Vue.js前端等)独立运行在不同的容器中,通过Docker或Kubernetes统一管理。以下是具体实现方式和关键原因: 1. 容器化部署的核心思路 每…...
从1号点到n号点最多经过k条边的最短距离
目录 解析方法思路代码解释代码逐行注释1. 头文件和常量定义:2.边的结构体:3.全局变量:4.Bellman-Ford算法实现:5.主函数: 注意事项代码含义为什么需要 backup[a]?举例说明关键点 总结 解析 要实现从1号点…...
模拟实战-用CompletableFuture优化远程RPC调用
实战场景 这是广州某500-900人互联网厂的面试原题 手写并发优化解决思路 我们要调用对方的RPC接口,我们的RPC接口每调用一次对方都会阻塞50ms 但是我们的业务要批量调用RPC,例如我们要批量调用1k次,我们不可能在for循环里面写1k次远程调用…...
【pinia状态管理配置】
pinia状态管理配置 安装main.ts引入自定义user仓库使用自定义仓库 安装 pnpm add piniamain.ts引入 // createPinia() 函数调用创建了一个新的 Pinia 实例。 // 这个实例是状态管理的核心,它将管理应用中所有的 store。 import { createPinia } from pinia app.us…...
SpringBoot 引⼊MybatisGenerator
SpringBoot 引⼊MybatisGenerator 1. 引入插件2. 添加generator.xml并修改3. 生成文件 1. 引入插件 <plugin><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-maven-plugin</artifactId><version>1.3.5</vers…...
在线销售数据集分析:基于Python的RFM数据分析方法实操训练
一、前言 个人练习,文章用于记录自己的学习练习过程,分享出来和大家一起学习。 数据集:在线销售数据集 分析方法:RFM分析方法 二、过程 1.1 库的导入与一些必要的初始设置 import pandas as pd import datetime import matplo…...
LeetCode - #197 Swift 实现找出温度更高的日期
网罗开发 (小红书、快手、视频号同名) 大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等…...
分析哲学:从 语言解剖到 思想澄清的哲学探险
分析哲学:从 语言解剖 到 思想澄清 的哲学探险 第一节:分析哲学的基本概念与公式解释 【通俗讲解,打比方来讲解!】 分析哲学,就像一位 “语言侦探”,专注于 “解剖语言”,揭示我们日常使用的语…...
C++【iostream】数据库的部分函数功能介绍
在 C 编程世界中,iostream 库扮演着举足轻重的角色,它是 C 标准库的核心组成部分,为程序提供了强大的输入输出功能。无论是简单的控制台交互,还是复杂的文件操作,iostream 库都能提供便捷高效的解决方案。本文将深入剖…...
金山打字游戏2010绿色版,Win7-11可用DxWnd完美运行
金山打字游戏2010绿色版,Win7-11可用DxWnd完美运行 链接:https://pan.xunlei.com/s/VOIAYCzmkbDfdASGJa_uLjquA1?pwd67vw# 进入游戏后,如果输入不了英文字母(很可能是中文输入状态),就按一下“Shift”键…...
洛谷[USACO08DEC] Patting Heads S
题目传送门 题目难度:普及/提高一 题面翻译 今天是贝茜的生日,为了庆祝自己的生日,贝茜邀你来玩一个游戏。 贝茜让 N N N ( 1 ≤ N ≤ 1 0 5 1\leq N\leq 10^5 1≤N≤105) 头奶牛坐成一个圈。除了 1 1 1 号与 N N N 号奶牛外࿰…...
讲清逻辑回归算法,剖析其作为广义线性模型的原因
1、逻辑回归算法介绍 逻辑回归(Logistic Regression)是一种广义线性回归分析模型。虽然名字里带有“回归”两字,但其实是分类模型,常用于二分类。既然逻辑回归模型是分类模型,为什么名字里会含有“回归”二字呢?这是因为其算法原…...
基于STM32的智能安防监控系统
1. 引言 随着物联网技术的普及,智能安防系统在家庭与工业场景中的应用日益广泛。本文设计了一款基于STM32的智能安防监控系统,集成人体感应、环境异常检测、图像识别与云端联动功能,支持实时报警、远程监控与数据回溯。该系统采用边缘计算与…...
八. Spring Boot2 整合连接 Redis(超详细剖析)
八. Spring Boot2 整合连接 Redis(超详细剖析) 文章目录 八. Spring Boot2 整合连接 Redis(超详细剖析)2. 注意事项和细节3. 最后: 在 springboot 中 , 整合 redis 可以通过 RedisTemplate 完成对 redis 的操作, 包括设置数据/获取数据 比如添加和读取数据 具体整…...
220.存在重复元素③
目录 一、题目二、思路三、解法四、收获 一、题目 给你一个整数数组 nums 和两个整数 indexDiff 和 valueDiff 。 找出满足下述条件的下标对 (i, j): i ! j, abs(i - j) < indexDiff abs(nums[i] - nums[j]) < valueDiff 如果存在,返回 true &a…...
【Linux】从硬件到软件了解进程
个人主页~ 从硬件到软件了解进程 一、冯诺依曼体系结构二、操作系统三、操作系统进程管理1、概念2、PCB和task_struct3、查看进程4、通过系统调用fork创建进程(1)简述(2)系统调用生成子进程的过程〇提出问题①fork函数②父子进程关…...
volatile变量需要减少读取次数吗
问题说明 本人在前期读Netty源码时看到这样一段源码和注释: private boolean invokeHandler() {// Store in local variable to reduce volatile reads.int handlerState this.handlerState;return handlerState ADD_COMPLETE || (!ordered && handlerS…...
红黑树的封装
一、封装思路 在 STL 中 map set 的底层就是封装了一棵红黑树。 其中连接红黑树和容器的是迭代器,map set 暴露出的接口都不是自己写的,而是红黑树写的,外部接口封装红黑树接口。 所以写出红黑树为 map set 写的接口,再在上层的…...
Java 泛型<? extends Object>
在 Java 泛型中,<? extends Object> 和 <?> 都表示未知类型,但它们在某些情况下有细微的差异。泛型的引入是为了消除运行时错误并增强类型安全性,使代码更具可读性和可维护性。 在 JDK 5 中引入了泛型,以消除编译时…...
TensorFlow简单的线性回归任务
如何使用 TensorFlow 和 Keras 创建、训练并进行预测 1. 数据准备与预处理 2. 构建模型 3. 编译模型 4. 训练模型 5. 评估模型 6. 模型应用与预测 7. 保存与加载模型 8.完整代码 1. 数据准备与预处理 我们将使用一个简单的线性回归问题,其中输入特征 x 和标…...
解码大数据的四个V:体积、速度、种类与真实性
解码大数据的四个V:体积、速度、种类与真实性 在大数据领域,有一个大家耳熟能详的概念——“四个V”:Volume(体积)、Velocity(速度)、Variety(种类)、Veracityÿ…...
【单层神经网络】基于MXNet的线性回归实现(底层实现)
写在前面 基于亚马逊的MXNet库本专栏是对李沐博士的《动手学深度学习》的笔记,仅用于分享个人学习思考以下是本专栏所需的环境(放进一个environment.yml,然后用conda虚拟环境统一配置即可)刚开始先从普通的寻优算法开始ÿ…...
深入解析 posix_spawn():高效的进程创建方式(中英双语)
深入解析 posix_spawn():高效的进程创建方式 1. 引言 在 Unix/Linux 系统中,传统的进程创建方式主要依赖 fork() 和 exec() 组合。但 fork() 在某些情况下可能存在性能瓶颈,特别是当父进程占用大量内存时,fork() 仍然需要复制整…...
2024-我的学习成长之路
因为热爱,无畏山海...
【Java异步编程】基于任务类型创建不同的线程池
文章目录 一. 按照任务类型对线程池进行分类1. IO密集型任务的线程数2. CPU密集型任务的线程数3. 混合型任务的线程数 二. 线程数越多越好吗三. Redis 单线程的高效性 使用线程池的好处主要有以下三点: 降低资源消耗:线程是稀缺资源,如果无限…...
前缀和多种基础
前缀和加法 #include<iostream> #include<algorithm> using namespace std; typedef long long ll; int n; const int N 1e310; int arr[N]; int pre[N]; int org[N]; int main(void) {cin >> n;for(int i 1 ; i < n ; i){cin >> arr[i];pre[i] …...
关于贪心学习的文笔记录
贪心,顾名思义就是越贪越好,越多越有易,他给我的感觉是,通常是求最大或最小问题,相比于动态规划贪心让人更加琢磨不透,不易看出方法,为此在这记录我所见过的题型和思维方法,以便回头…...
蓝桥杯思维训练营(三)
文章目录 题目详解680.验证回文串 II30.魔塔游戏徒步旅行中的补给问题观光景点组合得分问题 题目详解 680.验证回文串 II 680.验证回文串 II 思路分析:这个题目的关键就是,按照正常来判断对应位置是否相等,如果不相等,那么就判…...
农历2025开始 笔记
2/3 Hey everyone! The Chinese New Year holiday is over. I spent over ten days back home, and honestly, I feel even more exhausted than when I’m working. Yesterday, I drove for 13 hours straight and finally made it back. In a couple of days, I’ll officia…...
VR触感数据手套:触感反馈赋予虚拟交互沉浸式体验
随着动作捕捉技术的蓬勃发展,动捕数据手套成为了手部动作捕捉与虚拟交互的便捷工具,为人们打开了通往虚拟世界的新大门。在众多产品中,mHand Pro作为一款多功能兼具的VR动作捕捉数据手套,凭借其卓越的性能,在手部动作捕…...
6 [新一代Github投毒针对网络安全人员钓鱼]
0x01 前言 在Github上APT组织“海莲花”发布存在后门的提权BOF,通过该项目针对网络安全从业人员进行钓鱼。不过其实早在几年前就已经有人对Visual Studio项目恶意利用进行过研究,所以投毒的手法也不算是新的技术。但这次国内有大量的安全从业者转发该钓…...
基于LabVIEW的Modbus-RTU设备通信失败问题分析与解决
在使用 LabVIEW 通过 Modbus-RTU 协议与工业设备进行通信时,可能遇到无法正常发送或接收指令的问题。常见原因包括协议参数配置错误、硬件连接问题、数据帧格式不正确等。本文以某 RGBW 控制器调光失败为例,提出了一种通用的排查思路,帮助开发…...
【环境搭建】1.1源码下载与同步
目录 写在前面 一,系统要求 二,安装depot_tools 三,获取代码 四,代码同步 五,代码结构 写在前面 当前的开发背景是基于Google的开源Chromium,来开发Android设备的浏览器方案。 一,系统要…...
从理论到实践:Linux 进程替换与 exec 系列函数
个人主页:chian-ocean 文章专栏-Linux 前言: 在Linux中,进程替换(Process Substitution)是一个非常强大的特性,它允许将一个进程的输出直接当作一个文件来处理。这种技术通常用于Shell脚本和命令行操作中…...
增删改查(CRUD)操作
文章目录 MySQL系列:1.CRUD简介2.Create(创建)2.1单行数据全列插入2.2 单行数据指定插入2.3 多⾏数据指定列插⼊ 3.Retrieve(读取)3.1 Select查询3.1.1 全列查询3.1.2 指定列查询3.1.3 查询字段为表达式(都是临时表不会对原有表数据产生影响)…...
算法竞赛(Python)-堆栈
文章目录 一 基础知识二 题目有效的括号字符串解码 一 基础知识 堆栈(Stack):简称为栈。一种线性表数据结构,是一种只允许在表的一端进行插入和删除操作的线性表。 我们把栈中允许插入和删除的一端称为 「栈顶(top…...
【C++篇】位图与布隆过滤器
目录 一,位图 1.1,位图的概念 1.2,位图的设计与实现 1.5,位图的应用举例 1.4,位图常用应用场景 二,布隆过滤器 2.1,定义: 2.2,布隆过滤器的实现 2.3, 应…...
deeplabv3+街景图片语义分割,无需训练模型,看不懂也没有影响,直接使用,cityscapes数据集_6
目录 1、下载链接1.1、CSDN链接,含权重文件直接使用,建议直接下这个,还不限速。1.2 Github链接:2、下载代码,下载预训练好的权重3、预测代码4、像素提取,或者说类别提取5、文档部分内容截图6、其他数据处理…...
DeepSeek 原理解析:与主流大模型的差异及低算力优势
在人工智能大模型蓬勃发展的浪潮中,DeepSeek 以其独特的技术路线和出色的性能表现脱颖而出。与主流大模型相比,DeepSeek 不仅在技术原理上有着显著的差异,还展现出了在较低算力下达到 OpenAI API 水平的卓越能力。本文将深入剖析这些独特之处…...
OpenAI推出Deep Research带给我们怎样的启示
OpenAI 又发新产品了,这次是面向深度研究领域的智能体产品 ——「Deep Research」,貌似被逼无奈的节奏… 在技术方面,Deep Research搭载了优化后o3模型并通过端到端强化学习在多个领域的复杂浏览和推理任务上进行了训练。因没有更多的技术暴露…...
第三周 树
猫猫和企鹅 分数 10 全屏浏览 切换布局 作者 姜明欣 单位 河北大学 王国里有 nn 个居住区,它们之间有 n−1 条道路相连,并且保证从每个居住区出发都可以到达任何一个居住区,并且每条道路的长度都为 1。 除 1号居住区外,每个居…...
【挖矿——前缀和】
题目 代码 #include <bits/stdc.h> using namespace std; const int N 2e610; int l[N], r[N]; int n, m, ans; int main() {cin >> n >> m;for(int i 1; i < n; i){int p;cin >> p;if(p < 0) l[-p];else r[p];}for(int i 1; i < m; i)l[…...
整个 PVE 系统崩溃后,怎么恢复 PVE 给虚拟机分配的虚拟硬盘中的数据
背景 我有一块 ssd 用于 PVE 系统和 虚拟机 安装,还有一块 HDD 用来存储数据。这个HDD按照 把 PVE 下的机械硬盘(非SSD系统盘)分配给虚拟机使用 进行挂载和配置。主要过程是 PVE中 “数据中信” -> “存储” -> “添加” -> “目录…...
Java循环操作哪个快
文章目录 Java循环操作哪个快一、引言二、循环操作性能对比1、普通for循环与增强for循环1.1、代码示例 2、for循环与while循环2.1、代码示例 3、循环优化技巧3.1、代码示例 三、循环操作的适用场景四、使用示例五、总结 Java循环操作哪个快 一、引言 在Java开发中,…...
【C++ STL】vector容器详解:从入门到精通
【C STL】vector容器详解:从入门到精通 摘要:本文深入讲解C STL中vector容器的使用方法,涵盖常用函数、代码示例及注意事项,助你快速掌握动态数组的核心操作! 一、vector概述 vector是C标准模板库(STL&am…...
差值 dp 入门
引入 有一类问题:两个人交替选 n n n 个数 a [ 1 … n ] a[1 \dots n] a[1…n],要使得每个人分得的数大小之和相等(或差值尽可能小),同时尽可能保证分得的总金额尽可能大。 这类问题的解法之一是 dp。 有一个通用…...
使用mybatisPlus插件生成代码步骤及注意事项
使用mybatisPlus插件可以很方便的生成与数据库对应的PO对象,以及对应的controller、service、ImplService、mapper代码,生成这种代码的方式有很多,包括mybatis-plus提供的代码生成器,以及idea提供的代码生成器,无论哪一…...