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

前言

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

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

正文

题目(力扣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 表示未找到
};
相关推荐
meilindehuzi_a5 小时前
从零理解并实现 RAG:用 LangChain.js 构建语义检索问答系统
开发语言·javascript·langchain
世界哪有真情7 小时前
拿人类意识卡 AI?等于用 bug 验收正式产品
前端·人工智能·后端
:-)7 小时前
算法-归并排序
java·开发语言·数据结构·算法·排序算法
鱼樱前端7 小时前
别再自己拼凑了!这款 Vue 3.5+ 严肃产品级组件库,把 AI 对话、工作流和复杂数据全包了!
javascript·vue.js·github
Listen·Rain8 小时前
Vue3:setup详解
前端·javascript·vue.js
Patrick_Wilson8 小时前
修好 bug 只是开始:一次由监控需求反向重构日志结构的复盘
前端·监控·数据可视化
kyriewen9 小时前
面试官让我优化一个卡死的表格,我打开 Claude 五秒重写,他放下咖啡问我要不要来当组长
前端·javascript·面试
小粉粉hhh9 小时前
React(二)——dom、组件间通信、useEffect
前端·javascript·react.js
Csvn9 小时前
🤖 AI 生成前端代码的 5 个典型翻车现场及修复指南
前端
IT_陈寒10 小时前
SpringBoot自动装配坑了我一天,原来问题出在这
前端·人工智能·后端