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

LeetCode704.二分查找及二分法

每日一题:LeetCode704.二分查找

  • LeetCode704.二分查找
    • 知识点:二分法
    • 解题
    • 代码

LeetCode704.二分查找

问题描述:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1

示例 1:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

示例 2:

输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

提示:

  1. 你可以假设 nums 中的所有元素是不重复的。
  2. n 将在 [1, 10000]之间。
  3. nums 的每个元素都将在 [-9999, 9999]之间。

知识点:二分法

  1. 定义:二分查找算法也称折半搜索算法,对数搜索算法,是一种在有序数组中查找某一特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半,时间复杂度是log(n)

    1. 一是有序数组(这里可能是整体有序比如[1,2,3,4,5],也有可能是局部有序比如[4,5,1,2,3]),
    2. 二是特定元素(也有可能是满足特定的条件)。由定义我们大概就知道了二分法的应用场景,在有序数组中找特定值都可以考虑用二分法
  2. 二分法的步骤
    ​我们要在一组升序的数组找一个数的下标,那我们肯定是先拿中间的与他进行比较,比较大小的判断,其实就相当于是这个性质,且这个 性质满足二段性,将大于和小于我们要查找的值分为两段,而我们的查找结果就是分界点

  3. 二分法的使用条件:①上下界确定 ②区间内有序(也可以是局部有序)

解题

思路:①这道题目的前提是数组为有序数组,同时题目还强调数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的,这些都是使用二分法的前提条件②二分查找涉及的很多的边界条件,逻辑比较简单,但就是写不好。例如到底是 while(left < right) 还是 while(left <= right),到底是right = middle呢,还是要right = middle - 1呢?③写二分法,区间的定义一般为两种,左闭右闭即[left, right],或者左闭右开即[left, right)

  1. 第一种写法,定义 target 是在一个在左闭右闭的区间里,也就是[left, right] (这个很重要非常重要)
    区间的定义这就决定了二分法的代码应该如何写,因为定义target在[left, right]区间,所以有如下两点:

    • while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
    • if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
      例如在数组:1,2,3,4,7,9,10中查找元素2,如图所示:
  2. 第二种方法

    如果说定义 target 是在一个在左闭右开的区间里,也就是[left, right) ,那么二分法的边界处理方式则截然不同。
    有如下两点:

    • while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
    • if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]

    在数组:1,2,3,4,7,9,10中查找元素2,如图所示:(注意和方法一的区别

代码

python:(版本一)左闭右闭区间

class Solution:def search(self, nums: List[int], target: int) -> int:left, right = 0, len(nums) - 1  # 定义target在左闭右闭的区间里,[left, right]while left <= right:mid = (left + right)/2if nums[middle] > target:right = middle - 1  # target在左区间,所以[left, middle - 1]elif nums[middle] < target:left = middle + 1  # target在右区间,所以[middle + 1, right]else:return middle  # 数组中找到目标值,直接返回下标return -1  # 未找到目标值

python:(版本二)左闭右开区间

class Solution:def search(self, nums: List[int], target: int) -> int:left, right = 0, len(nums)  # 定义target在左闭右开的区间里,即:[left, right)while left < right:  # 因为left == right的时候,在[left, right)是无效的空间,所以使用 <mid = (left + right)/2if nums[middle] > target:right = middle  # target 在左区间,在[left, middle)中elif nums[middle] < target:left = middle + 1  # target 在右区间,在[middle + 1, right)中else:return middle  # 数组中找到目标值,直接返回下标return -1  # 未找到目标值

C++:(版本一)左闭右闭区间

class Solution {
public:int search(vector<int>& nums, int target) {int left = 0;int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]while (left <= right) { // 当left==right,区间[left, right]依然有效,所以用 <=int middle = (left + right)/2;// 防止溢出 等同于(left + right)/2if (nums[middle] > target) {right = middle - 1; // target 在左区间,所以[left, middle - 1]} else if (nums[middle] < target) {left = middle + 1; // target 在右区间,所以[middle + 1, right]} else { // nums[middle] == targetreturn middle; // 数组中找到目标值,直接返回下标}}// 未找到目标值return -1;}
};

C++:(版本二)左闭右开区间

class Solution {
public:int search(vector<int>& nums, int target) {int left = 0;int right = nums.size(); // 定义target在左闭右开的区间里,即:[left, right)while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间,所以使用 <int middle = (left + right)/2;if (nums[middle] > target) {right = middle; // target 在左区间,在[left, middle)中} else if (nums[middle] < target) {left = middle + 1; // target 在右区间,在[middle + 1, right)中} else { // nums[middle] == targetreturn middle; // 数组中找到目标值,直接返回下标}}// 未找到目标值return -1;}
};
http://www.lryc.cn/news/234699.html

相关文章:

  • 2023年R1快开门式压力容器操作证模拟考试题库及R1快开门式压力容器操作理论考试试题
  • 探索NLP中的核心架构:编码器与解码器的区别
  • 解决:Error: Missing binding xxxxx\node_modules\node-sass\vendor\win32-x64-83\
  • 科研学习|科研软件——面板数据、截面数据、时间序列数据的区别是什么?
  • 【UE5】物体沿样条线移动
  • Qt控件按钮大全
  • 软件工程--软件过程学习笔记
  • 高校教师资格证备考
  • Git通过rebase合并多个commit
  • ROS 学习应用篇(八)ROS中的坐标变换管理之tf广播与监听的编程实现
  • 计算机算法分析与设计(23)---二分搜索算法(C++)
  • 前置语音群呼与语音机器人群呼哪个更好
  • 『Element Plus の 百科大全』
  • P3879 [TJOI2010] 阅读理解- 字典树
  • upgrade k8s (by quqi99)
  • CronExpression
  • 释放机器人潜力,INDEMIND深耕底层技术
  • 【ES6标准入门】JavaScript中的模块Module语法的使用细节:export命令和imprt命令详细使用,超级详细!!!
  • 流量2----2
  • 人工智能发展前景
  • 编写程序,要求输入x的值,输出y的值。分别用(1)不嵌套的if语句(2)嵌套的if语句(3)if-else语句(4)switch语句。
  • AcWing 4520:质数 ← BFS
  • 00、计算机视觉入门与调优简介
  • .L0CK3D来袭:如何保护您的数据免受致命攻击
  • 多媒体ffmpeg学习教程
  • SELinux零知识学习十五、SELinux策略语言之客体类别和许可(9)
  • OpenSign:安全可靠的电子签名解决方案 | 开源日报 No.76
  • Linux | 进程间通信
  • Vue.js正式环境中配置多个请求的URL
  • 简单的 UDP 网络程序