二分法:清晰与模糊的边界,一看就会,一写就废

前言

二分查找是一种在有序数组中高效查找特定元素的算法。它在计算机科学领域有着广泛的应用,是算法学习中的重要内容之一。

在实际问题中,我们经常需要在大量的数据中快速定位某个目标元素。二分查找算法凭借其高效的性能和简洁的思想,成为解决这类问题的有力工具。通过对二分查找算法的深入理解和掌握,我们能够更好地应对各种查找相关的挑战,提高程序的运行效率和准确性。

正文

题目(力扣704)

复制代码
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
makefile 复制代码
示例 1:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
makefile 复制代码
示例 2:

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

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

思路

要对一个所有元素都是不重复的并且是有序的数组进行操作。我们可以想到使用二分查找的方法解决这个算法题,因为二分法的前提条件就是数组为有序数组 并且没有重复元素。如果有重复元素就会使返回小标不唯一。

在想到使用二分法时,便感觉这题稳了。但是总是会因为一些小细节导致无法得到预期结果。

在循环过程中是用left<right进行判断还是用left<=right进行判断循环是否继续;在nums[mid]<target时是让left=mid还是让left=mid-1;当nums[mid]>target时是让right=mid还是让right=mid-1

要怎么解决呢?首先对二分查找的区间进行分类讨论,一般分为两类。

第一类

定义 target 是在一个left,right区间里。

第一个困扰:在循环过程中是用left<right进行判断还是用left<=right进行判断循环是否继续。

其中唯一的区别就是left=right是否有意义。在left,right区间,left=right是有意义的,所以选择left<=right

第二个困扰:在nums[mid]<target时是让left=mid还是让left=mid-1;当nums[mid]>target时是让right=mid还是让right=mid-1

left,right区间里,nums[mid]<target

nums[mid]>target里的nums[mid]一定不等于target。所以应该选择为让left=mid-1和让right=mid-1

大体代码是这样的

javascript 复制代码
var search = function(nums, target) { 
    let left = 0; // 初始化左指针为 0
    let right = nums.length - 1; // 初始化右指针为数组的最后一个元素的索引
    let mid;

    while (left <= right) { // 只要左指针不超过右指针,就继续循环
        mid = Math.floor((right - left) / 2) + left; // 计算中间位置(可以防止溢出)
        if (nums[mid] === target) { // 如果中间位置的元素值等于目标值
            return mid; // 返回中间位置(即找到目标)
        } else if (nums[mid] > target) { // 如果中间位置的元素值大于目标值
            right = mid - 1; // 将右指针移动到中间位置的左侧
        } else { // 如果中间位置的元素值小于目标值
            left = mid + 1; // 将左指针移动到中间位置的右侧
        }
    }
    return -1; // 如果循环结束都没有找到目标值,返回 -1 表示未找到
};

第二类

定义 target 是在一个[left,right)区间里。

left=right在这个区间内并没有意义。所以用left<right对循环进行判断。

在[left,right)区间nums[mid]<target时,nums[mid]一定不等于target,所以让left=mid-1,但是因为右边为开区间所以在nums[mid]>target时,nums[mid]可能等于target,所以让right=mid

大体代码为

javascript 复制代码
var search = function(nums, target) { 
    // 初始化左指针为 0
    let left = 0; 
    // 初始化右指针为数组的长度(注意这里是 nums.length,而不是 nums.length - 1)
    let right = nums.length; 
    let mid; 

    while (left < right) { // 当左指针小于右指针时,继续循环
        mid = Math.floor((right - left) / 2) + left; // 计算中间位置(可以防止溢出)

        if (nums[mid] === target) { // 如果中间位置的元素等于目标值
            return mid; // 返回中间位置(即找到目标)
        } else if (nums[mid] > target) { // 如果中间位置的元素大于目标值
            right = mid; // 将右指针移动到中间位置
        } else { // 如果中间位置的元素小于目标值
            left = mid + 1; // 将左指针移动到中间位置的右侧
        }
    }
    return -1; // 如果循环结束都没有找到目标值,返回 -1 表示未找到
};
相关推荐
橙子家5 小时前
浏览器缓存之【身份与会话管理】:Cookies 和 Private state tokens
前端
To_OC6 小时前
LC 49 字母异位词分组:想到哈希表很简单,选对 key 才是精髓
javascript·算法·leetcode
最新资讯动态6 小时前
HDC 2026 | 对话鲸鸿动能:存量时代,品牌如何夺回营销“主动权”?
前端
最新资讯动态6 小时前
游戏出海,从产品走向体系
前端
最新资讯动态6 小时前
20人团队跑出百万DAU、大厂也来抢量:谁在鸿蒙生态跑出加速度
前端
最新资讯动态6 小时前
千万开发者背后,鸿蒙商业化的B面
前端
爱勇宝8 小时前
AI 时代:智商决定起点,情商决定走多远
前端·ai编程
kyriewen8 小时前
用了半年 Claude Code 后,我尝试关掉它写了一周代码——结果比想象中严重
前端·javascript·ai编程
IT_陈寒9 小时前
Vite的静态资源打包让我熬夜到三点,这坑千万别跳
前端·人工智能·后端
山河木马10 小时前
矩阵专题0-webGL中的矩阵
javascript·webgl·计算机图形学