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

数据结构与算法再探(七)查找-排序

查找

一、二分查找

二分查找是一种高效的查找算法,适用于在已排序的数组或列表中查找特定元素。它通过将搜索范围逐步减半来快速定位目标元素。理解二分查找的“不变量”和选择左开右闭区间的方式是掌握这个算法的关键。

二分查找关键点

不变量

在二分查找中,不变量是指在每一步迭代中保持不变的条件。对于二分查找来说,不变量通常是:目标值在当前搜索范围内:在每次迭代中目标值始终位于 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);}
};

相关文章:

数据结构与算法再探(七)查找-排序

查找 一、二分查找 二分查找是一种高效的查找算法&#xff0c;适用于在已排序的数组或列表中查找特定元素。它通过将搜索范围逐步减半来快速定位目标元素。理解二分查找的“不变量”和选择左开右闭区间的方式是掌握这个算法的关键。 二分查找关键点 不变量 在二分查找中&a…...

【C语言】指针(5)

前言&#xff1a;上篇文章的末尾我们使用了转移表来解决代码冗余的问题&#xff0c;那我们还有没有什么办法解决代码冗余呢&#xff1f;有的这就是接下来要说的回调函数。 往期文章: 指针1 指针2 指针3 指针4 文章目录 一&#xff0c;回调函数二&#xff0c;qsort实现快速排序1…...

大数据组件(四)快速入门实时数据湖存储系统Apache Paimon(2)

Paimon的下载及安装&#xff0c;并且了解了主键表的引擎以及changelog-producer的含义参考&#xff1a; 大数据组件(四)快速入门实时数据湖存储系统Apache Paimon(1) 利用Paimon表做lookup join&#xff0c;集成mysql cdc等参考&#xff1a; 大数据组件(四)快速入门实时数据…...

PLC通讯

PPI通讯 是西门子公司专为s7-200系列plc开发的通讯协议。内置于s7-200 CPU中。PPI协议物理上基于RS-485口&#xff0c;通过屏蔽双绞线就可以实现PPI通讯。PPI协议是一种主-从协议。主站设备发送要求到从站设备&#xff0c;从站设备响应&#xff0c;从站不能主动发出信息。主站…...

前端js进阶,ES6语法,包详细

进阶ES6 作用域的概念加深对js理解 let、const申明的变量&#xff0c;在花括号中会生成块作用域&#xff0c;而var就不会生成块作用域 作用域链本质上就是底层的变量查找机制 作用域链查找的规则是:优先查找当前作用域先把的变量&#xff0c;再依次逐级找父级作用域直到全局…...

Scrum方法论指导下的Deepseek R1医疗AI部署开发

一、引言 1.1 研究背景与意义 在当今数智化时代&#xff0c;软件开发方法论对于项目的成功实施起着举足轻重的作用。Scrum 作为一种广泛应用的敏捷开发方法论&#xff0c;以其迭代式开发、快速反馈和高效协作的特点&#xff0c;在软件开发领域占据了重要地位。自 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 项目背景 在信息高速流通的当下&#xff0c;新闻媒体行业每天都要处理和传播海量信息。传统的新闻管理模式依赖人工操作&#xff0c;在新闻采集、编辑、发布以及后续管理等环节中&#xff0c;不仅效率低下&#xff0c;而且容易出现人为失误。同时&#xff0…...

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、反射机制定义及作用 反射是允许程序在运行时检查和操作类、方法、属性等的机制&#xff0c;能够动态地获取信息、调用方法等。换句话说&#xff0c;在编写程序时&#xff0c;不需要知道要操作的类的具体信息&#xff0c;而是在程序运行时获取和使用。 2、反射机制…...

java实现二维码图片生成和编解码

java实现二维码图片生成和编解码 在wutool中&#xff0c;封装了二维码工具类&#xff0c;基于google的zxing库&#xff0c;实现二维码图片生成、编码和解码。 关于wutool wutool是一个java代码片段收集库&#xff0c;针对特定场景提供轻量解决方案&#xff0c;只要按需选择代…...

Java基础常见的面试题(易错!!)

面试题一&#xff1a;为什么 Java 不支持多继承 Java 不支持多继承主要是为避免 “菱形继承问题”&#xff08;又称 “钻石问题”&#xff09;&#xff0c;即一个子类从多个父类继承到同名方法或属性时&#xff0c;编译器无法确定该调用哪个父类的成员。同时&#xff0c;多继承…...

hugging face---transformers包

一、前言 不同于计算机视觉的百花齐放&#xff0c;不同网络适用不同情况&#xff0c;NLP则由Transformer一统天下。transformer是2017年提出的一种基于自注意力机制的神经网络架构&#xff0c;transformers库是hugging face社区创造的一个py库&#xff0c;通过该库可以实现统一…...

网络安全防护指南:筑牢网络安全防线(510)

一、网络安全的基本概念 &#xff08;一&#xff09;网络的定义 网络是指由计算机或者其他信息终端及相关设备组成的按照一定的规则和程序对信息收集、存储、传输、交换、处理的系统。在当今数字化时代&#xff0c;网络已经成为人们生活和工作中不可或缺的一部分。它连接了世…...

微信小程序实现拉卡拉支付

功能需求&#xff1a;拉卡拉支付&#xff08;通过跳转拉卡拉平台进行支付&#xff09;&#xff0c;他人支付&#xff08;通过链接进行平台跳转支付&#xff09; 1.支付操作 //支付 const onCanStartPay async (obj) > {uni.showLoading({mask: true})// 支付接口获取需要传…...

git从本地其他设备上fetch分支

在 Git 中&#xff0c;如果你想从本地其他设备上获取分支&#xff0c;可以通过以下几种方式实现。不过&#xff0c;需要注意的是&#xff0c;Git 本身是分布式版本控制系统&#xff0c;通常我们是从远程仓库&#xff08;如 GitHub、GitLab 等&#xff09;拉取分支&#xff0c;而…...

【干货教程】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框架 的 “捷邻小程序” 系统的设计与实现

大家好&#xff0c;今天要和大家聊的是一款基于 SSM框架 的 “捷邻小程序” 系统的设计与实现。项目源码以及部署相关事宜请联系我&#xff0c;文末附上联系方式。 项目简介 基于 SSM框架 的 “捷邻小程序” 系统设计与实现的主要使用者分为 管理员 和 用户&#xff0c;没有授…...

基于Springboot医院预约挂号小程序系统【附源码】

基于Springboot医院预约挂号小程序系统 效果如下&#xff1a; 小程序主页面 帖子页面 医生账号页面 留言内容页面 管理员主页面 用户管理页面 我的挂号页面 医生管理页面 研究背景 随着信息技术的飞速发展和互联网医疗的兴起&#xff0c;传统的医疗服务模式正面临着深刻的变…...

基于AVue的二次封装:快速构建后台管理系统的CRUD方案

基于AVue的二次封装&#xff1a;快速构建后台管理系统的CRUD方案 在开发后台管理系统时&#xff0c;表格是常见的组件之一。然而&#xff0c;使用原生的Element Plus实现CRUD&#xff08;增删改查&#xff09;功能往往需要编写大量重复代码&#xff0c;过程繁琐。即使借助类似…...

学会给AI搭系统,才是2026年最值钱的技能!收藏这份保姆级指南

文章对比了学习AI工具和使用AI系统两种方式&#xff0c;强调后者更具有长远价值。通过实例展示&#xff0c;搭建AI系统可以极大提高效率&#xff0c;且这种能力比单纯会使用AI工具更难掌握&#xff0c;因此更值得学习。文章提出“驾驭工程”概念&#xff0c;并给出普通人学习搭…...

Hunyuan-MT-7B部署教程:像素语言传送门在阿里云PAI-EAS平台的弹性推理服务部署

Hunyuan-MT-7B部署教程&#xff1a;像素语言传送门在阿里云PAI-EAS平台的弹性推理服务部署 1. 项目概述 像素语言传送门(Pixel Language Portal)是一款基于腾讯Hunyuan-MT-7B大语言模型构建的创新翻译工具。与传统翻译软件不同&#xff0c;它将语言转换过程设计成一场16-bit像…...

新手避坑指南:从零组装你的第一台Pixhawk四旋翼无人机(附PX4固件刷写教程)

新手避坑指南&#xff1a;从零组装你的第一台Pixhawk四旋翼无人机&#xff08;附PX4固件刷写教程&#xff09; 刚拆开快递箱时&#xff0c;那些散落的电机、飞控和电调模块可能会让你手足无措——这正是三年前我的真实写照。作为过来人&#xff0c;我整理出这份包含21个关键检查…...

从Xmodem到Ymodem:一个老牌文件传输协议在IoT设备调试中的“复活”实战

Ymodem协议在物联网设备调试中的高效实践 在物联网设备开发过程中&#xff0c;文件传输是一个看似简单却充满挑战的任务。当面对资源受限的嵌入式设备时&#xff0c;传统的网络协议栈往往显得过于庞大&#xff0c;而简单的串口通信又难以满足可靠性需求。正是在这样的背景下&am…...

如何快速掌握Steam成就管理:SteamAchievementManager终极实战指南

如何快速掌握Steam成就管理&#xff1a;SteamAchievementManager终极实战指南 【免费下载链接】SteamAchievementManager A manager for game achievements in Steam. 项目地址: https://gitcode.com/gh_mirrors/st/SteamAchievementManager SteamAchievementManager&am…...

别再手动重启了!IIS 7.5网站总挂?一招设置让应用程序池永不停止(附模块安装避坑)

IIS 7.5应用程序池自动恢复实战&#xff1a;告别半夜救火的运维噩梦 凌晨三点&#xff0c;服务器监控突然告警——网站又挂了。你强撑睡眼连上服务器&#xff0c;发现IIS应用程序池不知何时已经停止。这已经是本月第七次了。对于中小企业的运维人员或个人站长来说&#xff0c;这…...

如何用Arduino库实现PZEM-004T v3.0电能监测?完整指南解析

如何用Arduino库实现PZEM-004T v3.0电能监测&#xff1f;完整指南解析 【免费下载链接】PZEM-004T-v30 Arduino library for the Updated PZEM-004T v3.0 Power and Energy meter 项目地址: https://gitcode.com/gh_mirrors/pz/PZEM-004T-v30 PZEM-004T v3.0电能监测仪A…...

3个步骤,让顽固窗口乖乖听话:WindowResizer窗口调整魔法

3个步骤&#xff0c;让顽固窗口乖乖听话&#xff1a;WindowResizer窗口调整魔法 【免费下载链接】WindowResizer 一个可以强制调整应用程序窗口大小的工具 项目地址: https://gitcode.com/gh_mirrors/wi/WindowResizer 你是否曾经遇到过这样的烦恼&#xff1f;某个重要软…...

TongWeb部署实战:如何用Domain域搞定应用隔离、故障隔离与集群扩展?

TongWeb Domain域实战&#xff1a;应用隔离与集群扩展的架构艺术 在服务器资源有限而业务需求无限的矛盾中&#xff0c;如何优雅地实现应用隔离与资源扩展&#xff1f;这就像在一栋大楼里既要保证每个住户的隐私&#xff0c;又要确保公共设施的高效共享。TongWeb的Domain域设计…...

别再只盯着RCE了:Aria2 RPC接口的任意文件写入漏洞,手把手教你复现与本地环境搭建

深入解析Aria2 RPC接口的任意文件写入漏洞&#xff1a;从环境搭建到原理分析 在开源下载工具领域&#xff0c;Aria2凭借其轻量级、多协议支持的特性赢得了众多技术用户的青睐。然而&#xff0c;正是这样一个看似简单的工具&#xff0c;其RPC接口却隐藏着可能被恶意利用的安全隐…...