数据结构与算法再探(七)查找-排序
查找
一、二分查找
二分查找是一种高效的查找算法,适用于在已排序的数组或列表中查找特定元素。它通过将搜索范围逐步减半来快速定位目标元素。理解二分查找的“不变量”和选择左开右闭区间的方式是掌握这个算法的关键。
二分查找关键点
不变量
在二分查找中,不变量是指在每一步迭代中保持不变的条件。对于二分查找来说,不变量通常是:目标值在当前搜索范围内:在每次迭代中目标值始终位于 left 和 right 指针之间。如在查找一个值 target并且当前的搜索范围是 arr[left]- arr[right],那么我们可以保证如果 arr[left]≤target≤arr[right],则 target 一定在这个范围内。
区间定义
二分查找时区间的左右端取开区间还是闭区间在绝大多数时候都可以,二分查找中的左闭右开和左闭右闭的本质区别主要体现在搜索范围的定义和边界处理上。这种选择会影响算法的实现细节、边界条件的处理以及最终的查找结果。
二分查找实现
1)左闭右闭区间 [left, right]
定义:left 和 right 都是闭合的,表示搜索范围包括 left 和 right。当 left 等于 right 时,搜索范围仍然包含 right。在计算中间值时,使用 mid = left + (right - left) / 2。但是:这种都是闭区间可能会导致重复元素的处理变得复杂,特别是在查找第一个或最后一个出现的元素时。
int binarySearchClosed(const std::vector<int>& arr, int target) {int left = 0;int right = arr.size() - 1;while (left <= right) {int mid = left + (right - left) / 2;if (arr[mid] == target) {return mid; // 找到目标值} else if (arr[mid] < target) {left = mid + 1; // 在右半部分继续查找} else {right = mid - 1; // 在左半部分继续查找}}return -1; // 未找到目标值
}
2)左闭右开区间 [left, right)
left 是闭合的,right 是开合的,表示搜索范围包括 left,但不包括 right。当 left 等于 right 时,搜索范围不包含 right,因此 right 的值是 arr.size()。并且在更新 right 时使用 right = mid。优点:避免了中间元素重复的情况,特别适合查找插入位置。逻辑上更容易处理边界条件,特别是在处理空数组或查找插入位置时。但是没有左闭右闭直观。
int binarySearchOpen(const std::vector<int>& arr, int target) {int left = 0;int right = arr.size(); // 注意这里是 arr.size()while (left < right) { // 注意这里是 left < rightint mid = left + (right - left) / 2;if (arr[mid] == target) {return mid; // 找到目标值} else if (arr[mid] < target) {left = mid + 1; // 在右半部分继续查找} else {right = mid; // 在左半部分继续查找}}return -1; // 未找到目标值
}
二分区间查找
34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target,返回 [-1, -1]。你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
class Solution {// lower_bound 返回最小的满足 nums[i] >= target的下标 i// 如果数组为空,或者所有数都 <target,则返回nums.size()int lower_bound(vector<int>& nums, int target) {int left = 0, right = (int) nums.size() - 1; // 闭区间while (left <= right) {// 循环不变量:nums[left-1] < target nums[right+1] >= targetint mid = left + (right - left) / 2;if (nums[mid] >= target) {right = mid - 1; } else {left = mid + 1; }}// 循环结束后 left = right+1// 此时 nums[left-1] < target 而 nums[left] = nums[right+1] >= target// 所以 left 就是第一个 >= target 的元素下标return left;}
public:vector<int> searchRange(vector<int>& nums, int target) {int start = lower_bound(nums, target);if (start == nums.size() || nums[start] != target) {return {-1, -1}; // nums 中没有 target}// 如果 start 存在,那么 end 必定存在int end = lower_bound(nums, target + 1) - 1;return {start, end};}
};
二、深度搜索
沿分支尽可能深入,到达叶子节点后回溯,继续探索其他分支。
113. 路径总和 II - 力扣(LeetCode)
class Solution:def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:def dfs(node, path, current_sum):if not node:returncurrent_sum += node.valpath.append(node.val)if not node.left and not node.right and current_sum == targetSum:result.append(list(path))dfs(node.left, path, current_sum)dfs(node.right, path, current_sum)path.pop() # 回溯result = []dfs(root, [], 0)return result
三、广度搜索
按层级逐层遍历,先处理离根节点最近的节点,可以使用队列(FIFO)存储待访问节点。
102. 二叉树的层序遍历 - 力扣(LeetCode)
BFS层次遍历
class Solution:def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:if root is None:return []ans=[]q=deque([root])while q:vals=[]for _ in range(len(q)):node=q.popleft()vals.append(node.val)if node.left: q.append(node.left)if node.right:q.append(node.right)ans.append(vals)return ans
200. 岛屿数量 - 力扣(LeetCode)
BFS
def numIslands(grid):if not grid:return 0rows, cols = len(grid), len(grid[0])count = 0from collections import dequefor i in range(rows):for j in range(cols):if grid[i][j] == '1':queue = deque([(i, j)])grid[i][j] = '0' # 标记为已访问while queue:x, y = queue.popleft()for dx, dy in [(-1,0), (1,0), (0,-1), (0,1)]:nx, ny = x + dx, y + dyif 0 <= nx < rows and 0 <= ny < cols and grid[nx][ny] == '1':queue.append((nx, ny))grid[nx][ny] = '0'count += 1return count
DFS
def numIslands(grid):def dfs(x, y):if 0 <= x < rows and 0 <= y < cols and grid[x][y] == '1':grid[x][y] = '0'dfs(x+1, y)dfs(x-1, y)dfs(x, y+1)dfs(x, y-1)rows = len(grid)if rows == 0:return 0cols = len(grid[0])count = 0for i in range(rows):for j in range(cols):if grid[i][j] == '1':dfs(i, j)count += 1return count
排序
排序算法总结-CSDN博客
数组中第K大元素
215. 数组中的第K个最大元素 - 力扣(LeetCode)
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
要在时间复杂度为O(n) 的情况下找到数组中第k个最大的元素,可以使用快速选择(Quickselect)算法。这个算法的思想与快速排序相似,但它只关注找到第k 个最大的元素,而不是对整个数组进行排序。(排序后return nums[nums.size() - k]; O(nlogn))
选择基准元素:从数组中随机选择一个基准元素。(快排一趟确定一个元素的位置)
分区操作:将数组分为两部分,左侧是小于基准的元素,右侧是大于基准的元素。
判断基准位置:如果基准元素的位置正好是n−k(因为我们需要找到第k 个最大的元素),那么这个元素就是我们要找的元素。如果基准元素的位置大于n−k,则第k 个最大的元素在左侧部分。
如果基准元素的位置小于n−k,则第k 个最大的元素在右侧部分。
class Solution {
public:int quickselect(vector<int> &nums, int l, int r, int k) {if (l == r)return nums[k];int partition = nums[l], i = l - 1, j = r + 1;while (i < j) {do i++; while (nums[i] < partition);do j--; while (nums[j] > partition);if (i < j)swap(nums[i], nums[j]);}if (k <= j)return quickselect(nums, l, j, k);else return quickselect(nums, j + 1, r, k);}int findKthLargest(vector<int> &nums, int k) {int n = nums.size();return quickselect(nums, 0, n - 1, n - k);}
};
相关文章:
数据结构与算法再探(七)查找-排序
查找 一、二分查找 二分查找是一种高效的查找算法,适用于在已排序的数组或列表中查找特定元素。它通过将搜索范围逐步减半来快速定位目标元素。理解二分查找的“不变量”和选择左开右闭区间的方式是掌握这个算法的关键。 二分查找关键点 不变量 在二分查找中&a…...
【C语言】指针(5)
前言:上篇文章的末尾我们使用了转移表来解决代码冗余的问题,那我们还有没有什么办法解决代码冗余呢?有的这就是接下来要说的回调函数。 往期文章: 指针1 指针2 指针3 指针4 文章目录 一,回调函数二,qsort实现快速排序1…...
大数据组件(四)快速入门实时数据湖存储系统Apache Paimon(2)
Paimon的下载及安装,并且了解了主键表的引擎以及changelog-producer的含义参考: 大数据组件(四)快速入门实时数据湖存储系统Apache Paimon(1) 利用Paimon表做lookup join,集成mysql cdc等参考: 大数据组件(四)快速入门实时数据…...
PLC通讯
PPI通讯 是西门子公司专为s7-200系列plc开发的通讯协议。内置于s7-200 CPU中。PPI协议物理上基于RS-485口,通过屏蔽双绞线就可以实现PPI通讯。PPI协议是一种主-从协议。主站设备发送要求到从站设备,从站设备响应,从站不能主动发出信息。主站…...
前端js进阶,ES6语法,包详细
进阶ES6 作用域的概念加深对js理解 let、const申明的变量,在花括号中会生成块作用域,而var就不会生成块作用域 作用域链本质上就是底层的变量查找机制 作用域链查找的规则是:优先查找当前作用域先把的变量,再依次逐级找父级作用域直到全局…...
Scrum方法论指导下的Deepseek R1医疗AI部署开发
一、引言 1.1 研究背景与意义 在当今数智化时代,软件开发方法论对于项目的成功实施起着举足轻重的作用。Scrum 作为一种广泛应用的敏捷开发方法论,以其迭代式开发、快速反馈和高效协作的特点,在软件开发领域占据了重要地位。自 20 世纪 90 …...
LINUX安装使用Redis
参考 Install Redis on Linux | Docs 安装命令 sudo apt-get install -y lsb-release curl gpgcurl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpgsudo chmod 644 /usr/share/keyrings/redis-archive-keyrin…...
基于java新闻管理系统,推荐一款开源cms内容管理系统ruoyi-fast-cms
一、项目概述 1.1 项目背景 在信息高速流通的当下,新闻媒体行业每天都要处理和传播海量信息。传统的新闻管理模式依赖人工操作,在新闻采集、编辑、发布以及后续管理等环节中,不仅效率低下,而且容易出现人为失误。同时࿰…...
054 redisson
文章目录 使用Redisson演示可重入锁读写锁信号量闭锁获取三级分类redisson分布式锁 package com.xd.cubemall.product.config;import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.context…...
【数据结构】(12) 反射、枚举、lambda 表达式
一、反射 1、反射机制定义及作用 反射是允许程序在运行时检查和操作类、方法、属性等的机制,能够动态地获取信息、调用方法等。换句话说,在编写程序时,不需要知道要操作的类的具体信息,而是在程序运行时获取和使用。 2、反射机制…...
java实现二维码图片生成和编解码
java实现二维码图片生成和编解码 在wutool中,封装了二维码工具类,基于google的zxing库,实现二维码图片生成、编码和解码。 关于wutool wutool是一个java代码片段收集库,针对特定场景提供轻量解决方案,只要按需选择代…...
Java基础常见的面试题(易错!!)
面试题一:为什么 Java 不支持多继承 Java 不支持多继承主要是为避免 “菱形继承问题”(又称 “钻石问题”),即一个子类从多个父类继承到同名方法或属性时,编译器无法确定该调用哪个父类的成员。同时,多继承…...
hugging face---transformers包
一、前言 不同于计算机视觉的百花齐放,不同网络适用不同情况,NLP则由Transformer一统天下。transformer是2017年提出的一种基于自注意力机制的神经网络架构,transformers库是hugging face社区创造的一个py库,通过该库可以实现统一…...
网络安全防护指南:筑牢网络安全防线(510)
一、网络安全的基本概念 (一)网络的定义 网络是指由计算机或者其他信息终端及相关设备组成的按照一定的规则和程序对信息收集、存储、传输、交换、处理的系统。在当今数字化时代,网络已经成为人们生活和工作中不可或缺的一部分。它连接了世…...
微信小程序实现拉卡拉支付
功能需求:拉卡拉支付(通过跳转拉卡拉平台进行支付),他人支付(通过链接进行平台跳转支付) 1.支付操作 //支付 const onCanStartPay async (obj) > {uni.showLoading({mask: true})// 支付接口获取需要传…...
git从本地其他设备上fetch分支
在 Git 中,如果你想从本地其他设备上获取分支,可以通过以下几种方式实现。不过,需要注意的是,Git 本身是分布式版本控制系统,通常我们是从远程仓库(如 GitHub、GitLab 等)拉取分支,而…...
【干货教程】Windows电脑本地部署运行DeepSeek R1大模型(基于Ollama和Chatbox)
文章目录 一、环境准备二、安装Ollama2.1 访问Ollama官方网站2.2 下载适用于Windows的安装包2.3 安装Ollama安装包2.4 指定Ollama安装目录2.5 指定Ollama的大模型的存储目录 三、选择DeepSeek R1模型四、下载并运行DeepSeek R1模型五、常见问题解答六、使用Chatbox进行交互6.1 …...
基于 SSM框架 的 “捷邻小程序” 系统的设计与实现
大家好,今天要和大家聊的是一款基于 SSM框架 的 “捷邻小程序” 系统的设计与实现。项目源码以及部署相关事宜请联系我,文末附上联系方式。 项目简介 基于 SSM框架 的 “捷邻小程序” 系统设计与实现的主要使用者分为 管理员 和 用户,没有授…...
基于Springboot医院预约挂号小程序系统【附源码】
基于Springboot医院预约挂号小程序系统 效果如下: 小程序主页面 帖子页面 医生账号页面 留言内容页面 管理员主页面 用户管理页面 我的挂号页面 医生管理页面 研究背景 随着信息技术的飞速发展和互联网医疗的兴起,传统的医疗服务模式正面临着深刻的变…...
基于AVue的二次封装:快速构建后台管理系统的CRUD方案
基于AVue的二次封装:快速构建后台管理系统的CRUD方案 在开发后台管理系统时,表格是常见的组件之一。然而,使用原生的Element Plus实现CRUD(增删改查)功能往往需要编写大量重复代码,过程繁琐。即使借助类似…...
业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
用机器学习破解新能源领域的“弃风”难题
音乐发烧友深有体会,玩音乐的本质就是玩电网。火电声音偏暖,水电偏冷,风电偏空旷。至于太阳能发的电,则略显朦胧和单薄。 不知你是否有感觉,近两年家里的音响声音越来越冷,听起来越来越单薄? —…...
Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...
【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
宇树科技,改名了!
提到国内具身智能和机器人领域的代表企业,那宇树科技(Unitree)必须名列其榜。 最近,宇树科技的一项新变动消息在业界引发了不少关注和讨论,即: 宇树向其合作伙伴发布了一封公司名称变更函称,因…...
