算法合集 | 二分查找法 | Leetcode 704.二分查找

138 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

前言

本系列文章主要会总结一些常见的算法题目以及算法的易错点,难点,以及一些万用的公式,并且结合实际的 Leetcode 题目来进行加深理解以及实际应用,算法这种东西,属于是一到用时方恨少的类型,在平时总结一些常见的简单算法,经常磨练自己的算法思维,对于日常的开发还是能有不少的帮助的。

  • 今天主要介绍一下二分法

什么是二分法

二分法在算法当中应该算是基础中的基础,二分法又可以叫做折半法,对于二分法下一个定义,就是:

  • 有序数组中查找特定元素的搜索算法。

为什么一定要是有序数组呢,下面来说一下二分法查找的一个过程,就能够了解了。二分法的查找思路如下:

  1. 从有序数组的中间开始进行搜索,如果中间元素为目标元素,就直接结束返回中间元素,否则执行下一步
  2. 如果目标元素大于中间元素,那么代表目标元素在大于中间元素的另一半区域,反之,如果目标元素小于中间元素,那么代表目标元素在小于中间元素的另一半区域。然后对另一半区域单独取出来,进行步骤 1 的操作。
  3. 如果一直查找到空数组还找不到目标元素,那么证明目标元素并不在数组当中

根据上面的查找过程,我们就能够知道数组元素为什么要求是有序的,因为需要通过排序后的中间值来判断目标元素的位置。

二分法的时间复杂度为 O(logn) 是要优于一般的循环暴力搜索的,所以在查找元素的过程当中相比较于暴力循环能够优化时间复杂度。

二分查找法的实际应用

二分法可以分为左闭右闭写法以及左闭右开写法,下面先介绍一下比较通用的左闭右闭的写法。

let left = 0 
let right = arr.length-1
while(left<=right) {
    let mid = (left+right)/2
    if(arr[mid]>target) {
        right = mid-1
    }
    else if(arr[mid]<target) {
        left = mid+1
    }
    else if(arr[mid]=target) {
        return mid
    }
}

上面这个是比较通用的一个二分法的模板,用于获取目标元素的下标,对于日常来写的时候,可能存在疑惑的地方就是 while 循环的条件处,应该是 left<right 还是 left<=right,还有一个就是下面的赋值处,是要赋值 mid 还是 mid-1.

其实这两种的区别就是上面提到的 左闭右闭 和 左闭右开 的两种写法的区别,下面就说一下区别在哪。

左闭右闭

对于一个有序数组 arr ,以及我们要在其中查找到目标元素 target,首先定义左边边界为 left = 0,定义右边边界为 right = arr.length-1

对于一个左闭右闭的区间来说,左边的left和右边的right都是能够取得到的,那么在循环的过程当中 left 和 right 相等的这种情况就是有意义的循环,比方说当前的数组为 [1, 1] 那么相等的这种情况,在这个区间当中,也是属于合法的情况。

对于下面的赋值,对于左闭右闭的区间来说,我们已经判断了 arr[mid] > target 那么 mid 这个位置上的值就不可能是目标值,并且边界是闭合边界能够取得到的,那么就应该是 mid - 1 和 mid + 1.

那么在解决了这两个关键的问题,就能够写出左闭右闭模式的二分查找,也就是和上面模板一样的写法。

左闭右开

那么还是对于一个有序数组 arr ,以及我们要在其中查找到目标元素 target,首先定义左边边界为 left = 0,定义右边边界为 right = arr.length-1

对于一个左闭右开的区间来说,左边的left能够取得到,但是右边的right是取不到的,那么在循环的过程当中 left 和 right 相等的这种情况就是无意义的循环,比方说当前的数组为 [1, 1] 那么相等的这种情况,在这个区间当中,很明显就是不合法的,因为不可能取得到 1,又取不到 1。

对于下面的赋值,对于左闭右开的区间来说,如果 arr[mid]<target 那么说明目标值是在中间的右边,并且区间的定义中,左边的闭的,也就是包含状态,等同于上面,所以要取 mid + 1,但是对于 arr[mid]>target这种情况来说,右边是一个开区间,本身就不包含 mid 这个值,所以就是等于 mid

那么在解决了这两个关键的问题,就能够写出左闭右开模式的二分查找。

Leetcode 704.二分查找

那么在总结了解题方法之后,再来看Leetcode的题目就简单了。

function search(nums: number[], target: number): number {
    let left = 0
    let right = nums.length - 1
    while (left <= right) {
        const mid = Math.floor((right - left) / 2) + left
        const num = nums[mid];
        if (num === target) {
            return mid;
        } else if (num > target) {
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }
    return -1;
};

图片.png