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

前言

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

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

正文

题目(力扣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 表示未找到
};
相关推荐
caperxi1 分钟前
前端开发中的防抖与节流
前端·javascript·html
霸气小男1 分钟前
react + antDesign封装图片预览组件(支持多张图片)
前端·react.js
susu10830189112 分钟前
前端css样式覆盖
前端·css
学习路上的小刘4 分钟前
vue h5 蓝牙连接 webBluetooth API
前端·javascript·vue.js
&白帝&4 分钟前
vue3常用的组件间通信
前端·javascript·vue.js
凌肖战5 分钟前
力扣上刷题之C语言实现(数组)
c语言·算法·leetcode
小白小白从不日白15 分钟前
react 组件通讯
前端·react.js
罗_三金25 分钟前
前端框架对比和选择?
javascript·前端框架·vue·react·angular
秋夫人31 分钟前
B+树(B+TREE)索引
数据结构·算法
Redstone Monstrosity32 分钟前
字节二面
前端·面试