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

前言

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

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

正文

题目(力扣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 表示未找到
};
相关推荐
Epiphany.5561 小时前
蓝桥杯备赛题目-----爆破
算法·职场和发展·蓝桥杯
Ticnix1 小时前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人1 小时前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl1 小时前
OpenClaw 深度技术解析
前端
崔庆才丨静觅1 小时前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人1 小时前
vue3使用jsx语法详解
前端·vue.js
YuTaoShao1 小时前
【LeetCode 每日一题】1653. 使字符串平衡的最少删除次数——(解法三)DP 空间优化
算法·leetcode·职场和发展
天蓝色的鱼鱼1 小时前
shadcn/ui,给你一个真正可控的UI组件库
前端
茉莉玫瑰花茶2 小时前
C++ 17 详细特性解析(5)
开发语言·c++·算法
布列瑟农的星空2 小时前
前端都能看懂的Rust入门教程(三)——控制流语句
前端·后端·rust