算法系列之新思路搞定二分法的边界问题

算法系列之新思路搞定二分法的边界问题

二分的思想虽然从在学校的时候就反复被考,但是真的写起来,细节很复杂,边界条件,循环条件,应不应该加一之类的老是出错。下面这是个传统的二分写法。给大家简单回忆一下,然后直接看后面的新写法。

var searchInsert = function(nums, target) {
    const len = nums.length;
    let left = 0;
    let right = len - 1;
    while(left <= right) {
        const mid = Math.floor((left + right) / 2);
        if (nums[mid] === target) {
            return mid;
        }
        if (nums[mid] > target) {
            right = mid - 1;
            continue;
        }
        if (nums[mid] < target) {
            left = mid + 1;
            continue;
        }
    }
    return left;
};

蓝红区域思想

下面思想来自于 “五点七边” https://www.bilibili.com/video/BV1d54y1q7k7
实践了两道题发现很好用。

在这里插入图片描述

我们把数组想成这样一个模型,前面区域是满足某种条件的蓝色,后面是红色,刚开始数组是没有颜色的,我们要求出的就是蓝红边界位置。

实现步骤:

  1. 生成两个指针 l 和 r,分别指向蓝色区域和红色区域,一开始分别在数组的边界外(-1,length)
  2. 循环,通过二分,两个指针快速向蓝红边界逼近,直到 l + 1 = r,则刚好到边界。
  3. 根据实际情况(问题定义和自己的区域条件定义)来看返回 l 还是 r。
l = -1, r = N
while l + 1 !== r
    m = Math.floor((l + r) / 2)
    if isBlue(m)
        l = m
    else
        r = m
return l or r

解题步骤

而我们拿到题目要做的就是:

  • 建模,划分蓝红区域,确认 isBlue()。
  • 确认返回 l 还是 r。
  • 套用算法模版。
  • (一些额外处理)

实例

以两道题作为例子来看。

第一道:
https://leetcode.cn/problems/binary-search/description/

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

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

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

思路:
很经典的一道二分题,我们首先划分蓝红区域,确定 isBlue 函数,蓝色区域就是小于目标值的区域,那这样到最后划分出分界点时,红色区域指针指向的就应该是目标值,如果不是的话就说明目标值不存在。

function search(nums: number[], target: number): number {
    let l = -1, r = nums.length;
    while (l + 1 < r) {
        let mid = Math.floor((l + r) / 2); 
        if (nums[mid] < target) {
            l = mid;
        } else {
            r = mid;
        }
    }

    return nums[r] === target ? r : -1;
};

在这里插入图片描述

第二道:
https://leetcode.cn/problems/search-insert-position/description/

题目:给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。

思路:和前一道没什么区别,只是说在找不到目标值的时候返回插入位置,那我们沿用上一道的思想,蓝色区域就是小于目标值的区域,那这样到最后划分出分界点时,红色区域指针指向的就应该是目标值或者是插入位置。

var searchInsert = function(nums, target) {
    let l = -1, r = nums.length;
    while (l + 1 < r) {
        mid = Math.floor((l + r) / 2);
        if (nums[mid] < target) {
            l = mid;
            continue;
        }
        r = mid;
    }
    return r;
};

几乎一摸一样是不是,证明这个思想根据不同问题需要,只需要微调返回的处理就可以。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值