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

leecoede 二分查找 题集

题目一:搜索旋转排序数组

整数数组 nums 按升序排列,数组中的值 互不相同 。

在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 向左旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 下标 3 上向左旋转后可能变为 [4,5,6,7,0,1,2] 。

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 1: 输入:nums = [4,5,6,7,0,1,2], target = 0 输出:4
示例 2: 输入:nums = [4,5,6,7,0,1,2], target = 3 输出:-1

注意,数组是升序排序之后进行左移的!!!所以需要思考移动之后的数组,会变成两个升序部分,所以二分法应用过程中需要注意左半部分和右半部分可能不是有序的,需要去判断一下target所在区间
class Solution {public int search(int[] nums, int target) {if(nums == null || nums.length == 0) {return -1;}int left = 0;int right = nums.length - 1;while(left <= right) {int mid = left + (right - left) / 2;if(nums[mid] == target) {return mid;}// 判断右半部分是否有序if(nums[mid] < nums[right]) {// 右半部分有序if(nums[mid] < target && nums[right] >= target) {// 目标值在右半部分left = mid + 1;} else {// 目标值在左半部分right = mid - 1;}} else {// 左半部分有序if(nums[left] <= target && nums[mid] > target) {// 目标值在左半部分right = mid - 1;} else {// 目标值在右半部分left = mid + 1;}}}return -1;}
}

核心思想

普通二分查找
在一个完全有序的数组中,通过不断将数组分为两半,判断目标值与中间值的关系,逐步缩小搜索范围。

每次迭代中,计算中间位置mid,比较nums[mid]与目标值target:

如果nums[mid] == target,返回mid。
如果nums[mid] < target,调整左边界left = mid + 1。
如果nums[mid] > target,调整右边界right = mid - 1。

旋转排序数组

  • 旋转后的数组不再是完全有序的,但总有一半是有序的。

  • 每次迭代中,通过判断nums[mid]与nums[left]和nums[right]的关系,确定哪一半是有序的。

  • 在有序的一半中,判断目标值是否在该区间内,从而调整搜索范围。

详细步骤

假设数组nums = [4, 5, 6, 7, 0, 1, 2],目标值target = 0:

初始化:

left = 0
right = 6
mid = (0 + 6) // 2 = 3
nums[mid] = nums[3] = 7

第一次循环:

判断右半部分是否有序:
nums[mid] < nums[right],即7 < 2,不成立,说明右半部分不是有序的。

因此,左半部分[4, 5, 6, 7]是有序的。

判断目标值是否在左半部分:
nums[left] <= target < nums[mid],即4 <= 0 < 7,不成立,说明目标值不在左半部分。

调整左边界:left = mid + 1 = 4

第二次循环:

mid = (4 + 6) // 2 = 5
nums[mid] = nums[5] = 1

判断右半部分是否有序:
nums[mid] < nums[right],即1 < 2,成立,说明右半部分[1, 2]是有序的。

判断目标值是否在右半部分:
nums[mid] < target <= nums[right],即1 < 0 <= 2,不成立,说明目标值不在右半部分。

调整右边界:right = mid - 1 = 4

第三次循环:

mid = (4 + 4) // 2 = 4
nums[mid] = nums[4] = 0
找到目标值,返回mid = 4

关键点

确定有序区间

每次迭代中,通过nums[mid]与nums[left]和nums[right]的关系,确定哪一半是有序的。

如果nums[mid] >= nums[left],左半部分[left, mid]是有序的。

如果nums[mid] < nums[right],右半部分[mid, right]是有序的。

在有序区间内查找
在有序的一半中,判断目标值是否在该区间内:
如果目标值在有序区间内,调整搜索范围到该区间。
否则,调整搜索范围到另一区间。

总结:

上述的搜索旋转数组查找本质上和二分查找区别不大,只是需要确定有序区间。

普通二分查找查找的数组已经是有序的,所以直接一半接一半查找即可。

旋转数组需要在对应有序的区间中进行二分查找,才能有效实现查找目标值。

如果不确定顺序直接二分查找:
  • 错误的搜索范围调整
    如果不判断哪一部分是有序的,直接根据nums[mid]与target的关系调整搜索范围,可能会将目标值排除在搜索范围之外,从而错过目标值。

  • 无法正确找到目标值
    由于数组不再是完全有序的,直接应用普通二分查找的逻辑可能会导致无法正确找到目标值,或者进入无限循环。"

题目二:搜索二维矩阵

给你一个满足下述两条属性的 m x n 整数矩阵

每行中的整数从左到右按非严格递增顺序排列。
每行的第一个整数大于前一行的最后一个整数。
给你一个整数 target ,如果 target 在矩阵中,返回 true ;否则,返回 false 。

//这是我感慨是思考的代码部分!!!
因为二维数组就是特殊的一维数组,将上述的升序的二维数组展开看可以得到一个呈升序的一维数组。m行n列,循环二次查询m行的n个数据就可以了

//这是我感慨是思考的代码部分
class Solution {public boolean searchMatrix(int[][] matrix, int target) {// 所以搜索的时候需要和二分查找差不多int m=matrix.length;//行数int n=matrix[0].length;//列数//在二维数组中,怎么定位某个元素?--->有m行需要查找的一维数组for(int i=0;i<m;i++){int left=0;int right=n-1;int mid=left+(right-left)/2;while(left<right){if(matrix[i][mid]==target){return true;}if(matrix[i][mid]>target){right=mid-1;mid=left+(right-left)/2;}if(matrix[i][mid]<target){left=mid+1;mid=left+(right-left)/2;}      }}return false;}
}

聪明如你,你肯定看出来了,上述二分条件写错了。

没错,我的上述代码条件应该是left<=right.

在二分查找中,循环条件的选择(left <= right 或 left < right)取决于如何处理边界情况。这两种条件都可以正确实现二分查找,但在逻辑上有一些细微的差别。

1. 循环条件 left <= right

使用 left <= right 作为循环条件时,循环会在 left 和 right 相等时仍然执行一次。这是为了确保当搜索区间缩小到一个元素时,仍然能够检查这个元素。

  • 逻辑解释
    初始化:left = 0,right = n - 1。
    循环条件:while (left <= right)。
    终止条件:当 left 超过 right 时,循环终止。

优点
简单直观:这种条件更直观,容易理解。
边界检查:确保当搜索区间缩小到一个元素时,仍然能够检查这个元素。

示例
假设数组 [1, 3, 5, 7],目标值 5:
初始:left = 0,right = 3,mid = 1,nums[mid] = 3。
第一次循环:3 < 5,left = 2。
第二次循环:left = 2,right = 3,mid = 2,nums[mid] = 5,找到目标值。

2. 循环条件 left < right

使用 left < right 作为循环条件时,循环会在 left 和 right 相等时停止。这意味着在某些情况下,你可能需要在循环外额外检查 left 或 right 的值。

  • 逻辑解释
    初始化:left = 0,right = n - 1。
    循环条件:while (left < right)。
    终止条件:当 left 等于 right 时,循环终止。

优点
减少一次循环:在某些情况下,可以减少一次不必要的循环。

缺点
额外检查:在循环外需要额外检查 left 或 right 的值,以确保不会错过目标值。

示例
假设数组 [1, 3, 5, 7],目标值 5
初始:left = 0,right = 3,mid = 1,nums[mid] = 3。

第一次循环:3 < 5,left = 2。

第二次循环:left = 2,right = 3,mid = 2,nums[mid] = 5,找到目标值。

如果目标值是 7

初始:left = 0,right = 3,mid = 1,nums[mid] = 3。

第一次循环:3 < 7,left = 2。

第二次循环:left = 2,right = 3,mid = 2,nums[mid] = 5。

循环终止:left = 2,right = 3,需要额外检查 nums[right]。

3. 通常选择 left <= right

“选择 left <= right
作为循环条件的原因是它更简单、更直观,且不需要在循环外进行额外的检查。这种条件确保了在搜索区间缩小到一个元素时,仍然能够检查这个元素,从而避免了遗漏目标值的可能性。”

修正后的代码
class Solution {public boolean searchMatrix(int[][] matrix, int target) {int m = matrix.length; // 行数if (m == 0) return false; // 如果矩阵为空,直接返回 falseint n = matrix[0].length; // 列数for (int i = 0; i < m; i++) {int left = 0;int right = n - 1;while (left <= right) { // 使用正确的循环条件int mid = left + (right - left) / 2;if (matrix[i][mid] == target) {return true; // 找到目标值,返回 true} else if (matrix[i][mid] > target) {right = mid - 1; // 目标值在左半部分} else {left = mid + 1; // 目标值在右半部分}}}return false; // 遍历所有行后未找到目标值,返回 false}
}

文章转载自:

http://ojz80ie4.sqqds.cn
http://GGgvzlos.sqqds.cn
http://s4WrRxjU.sqqds.cn
http://V09wbDGD.sqqds.cn
http://LQ2VAHZS.sqqds.cn
http://1HkVvpap.sqqds.cn
http://C4DFMkwO.sqqds.cn
http://uLLg6Xnf.sqqds.cn
http://fkBWWRoR.sqqds.cn
http://hPYkTJ2p.sqqds.cn
http://ILuDBZId.sqqds.cn
http://LwJF07sV.sqqds.cn
http://ds6Oa7uH.sqqds.cn
http://clyP6xVL.sqqds.cn
http://p7Hum0ON.sqqds.cn
http://KWXZ09Rp.sqqds.cn
http://cLuAKcFm.sqqds.cn
http://B6e75CBp.sqqds.cn
http://PKcsiXOs.sqqds.cn
http://dl9ply2a.sqqds.cn
http://8Rpo7ilo.sqqds.cn
http://kYYdN6Va.sqqds.cn
http://N2RdqLA4.sqqds.cn
http://KBIqzE7C.sqqds.cn
http://Jn6HdamY.sqqds.cn
http://kHTAW5NJ.sqqds.cn
http://sxoevT6u.sqqds.cn
http://6yV3xlyO.sqqds.cn
http://klJK1yyR.sqqds.cn
http://KHfAt9EH.sqqds.cn
http://www.dtcms.com/a/371952.html

相关文章:

  • 编写第一个程序-Ai8051U-32bit,Keil设置
  • Objective-C方法参数标签怎么设置
  • 国内外最新AI语言模型行情分析2025年9月最新内容
  • [数据结构] 栈和队列
  • 基于moduo库实现protobuf通信
  • Android开发-图像显示
  • OpenHarmony之设备风险管理平台(SecurityGuard)模块源码详解
  • Kotlin 协程之 Flow 的理解使用及源码解析
  • Vue2.x核心技术与实战(六)-Vuex
  • 认知篇#12:基于非深度学习方法的图像特征提取
  • 软考备考①
  • 信息安全工程师软考攻坚:第三章网络安全技术深度解析与实战训练
  • JDK17日期格式‘MMM’导致九月Sept/Sep格式化异常问题❗❗❗
  • Vulkan 学习(20)---- UniformBuffer 的使用
  • 微信小程序中实现AI对话、生成3D图像并使用xr-frame演示
  • 【不背八股】9.MySQL知识点汇总
  • MySQL6
  • 论文阅读:ICLR 2021 BAG OF TRICKS FOR ADVERSARIAL TRAINING
  • GD32自学笔记:4.ADC
  • LeetCode 522.最长特殊序列2
  • CentOS 7.2 虚机 ssh 登录报错在重启后无法进入系统
  • 腾讯混元 3D 2.0 Windows 便携版:低显存需求下的高效文/图生3D体验
  • 火山 RTC 引擎15 拉流 推流 地址生成器 、合流转推 开关
  • CesiumJS详解:打造专业级Web 3D地球仪与地图的JavaScript库
  • 数据结构:顺序表与链表
  • C++ 前缀和 高频笔试考点 实用技巧 牛客 DP34 [模板] 前缀和 题解 每日一题
  • kotlin - 平板分屏,左右拖动,2个Activity计算宽度,使用ActivityOptions、Rect(三)
  • 【软考架构】第七章 系统架构设计基础知识-7.2基于架构的软件开发方法:Architecture-Based Software Design,ABSD
  • Dify 从入门到精通(第 81/100 篇):Dify 的多模态模型监控(高级篇)
  • 2019年11月系统架构设计师真题及解析摘要