< 算法基础 之 二分查找 >

算法基础 之 二分查找

  • 前言
  • “ 二分查找 ” 原理及实现
  • 实际案例:
    • > 基础案例 - 搜索下标
      • 示例 1
      • 示例 2
    • 解决方案
    • > 进阶案例 - 搜索二维矩阵
      • 示例 1
      • 示例 2
    • 解决方案
  • 往期内容

前言

在开发中,我们常常会需要查找某个顺序存储结构中的内容时,我们常常会直接使用Javascript中已经封装好的函数去查找或者辅助查找。

但是也由于封装的特性,我们对这种封装好的函数,比较难以控制其复杂度,包括其内部执行语句的庸余部分难以把控。所以,在算法中,诞生了二分查找这个算法概念。

二分查找(Binary Search)也叫作折半查找。二分查找有两个要求,一个是数列有序,另一个是数列使用顺序存储结构(比如数组)。

“ 二分查找 ” 原理及实现

二分查找的实现原理非常简单,首要条件是要有一个有序的列表或者是一个顺序存储结构,如:数组,有序树状结构等。

但是如果没有,则该怎么办?可以使用排序算法进行排序,给二分查找创建先要条件。

升序数列为例,在数组 nums 中寻找目标值 target,对于特定下标 i,比较 nums[i]target 的大小:

  • 如果 nums[i] = target,则下标 i 即为要寻找的下标;

  • 如果 nums[i] > target,则 target 只可能在下标 i 的左侧,则需要将下次查找的结束下标 - 1;

  • 如果 nums[i] > target,则 target 只可能在下标 i 的右侧,则需要将下次查找的开始下标 + 1;

根据上面描述的理论,不难理解。

二分查找的做法是,定义查找的范围 [left,right],初始查找范围是整个数组。每次取查找范围的中点 mid,比较 nums[mid]target 的大小,如果相等则 mid 即为要寻找的下标,如果不相等则根据 nums[mid]target 的大小关系将查找范围缩小一半。

图解如下:

< 算法基础 之 二分查找 >_第1张图片

实际案例:

> 基础案例 - 搜索下标

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

本题取自 leetcode 算法基础

示例 1

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

示例 2

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

解决方案

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    // 方法一:循环比对, 代码简洁明了,但在数据量大且需要自定义排查逻辑时,不太合适。 适用于简单数组筛查或非顺序数据结构筛查
    return nums.indexOf(target)
	return nums.findIndex(target)
	
    // 方法二: 二分查找,代码
    let left = 0, right = nums.length - 1

    while(left <= right) {
        const mid = Math.floor((right + left) / 2) | Math.floor((right - left) / 2) + left
        const curVal = nums[mid]
        if(curVal === target) return mid
        if(curVal > target) right = mid - 1
        else left = mid + 1
    }

    return -1
};

注意事项: 这里我们可能会有疑惑的地方,就是中间值的取值了。由于left和right都代表的是元素的下标,所以在取中间值的时候,按道理只需right/2 | (right - left) / 2就行,但是这求的是 right下标值的中间值,并非要求的数组中间值下标。 所以需要再加上left的原本的下标值。即: ( left + right ) / 2 或 ( right - left ) / 2 + left

> 进阶案例 - 搜索二维矩阵

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:

  • 每行的元素从左到右升序排列。
  • 每列的元素从上到下升序排列。

本题取自 leetcode 算法

示例 1

< 算法基础 之 二分查找 >_第2张图片

输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5
输出:true
示例 2:

示例 2

< 算法基础 之 二分查找 >_第3张图片

输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 20
输出:false

解决方案

/**
 * @param {number[][]} matrix
 * @param {number} target
 * @return {boolean}
 */
var searchMatrix = function(matrix, target) {
    // 常规循环比对
    return matrix.some((item, i) => {
        if(item[0] > target) return false
        if(item[item.length - 1] < target) return false
        return item.indexOf(target) != -1
    })

    // 二分查找
    const getIndex = (nums = [], target = '') => {
        let left = 0, right = nums.length - 1
        while(left <= right) {
            let mid = Math.floor((left + right)/2)
            const val = nums[mid]
            if(val == target) return mid
            if(val > target) right = mid - 1
            else left = mid + 1
        }
        return -1
    }

    for(let cur of matrix) {
        if(cur.length == 0 ) continue
        let start = cur[0], end = cur[cur.length - 1]
        if(start <= target && end >= target && getIndex(cur, target) != -1) return true
    }
    return false
};

通过上述案例,相信各位卷王对 “ 二分查找 ”的概念也理解的七七八八了。 可以尝试去刷刷二分查找类型的算法题,巩固记忆吧!

点击跳转 LeetCode “ 二分查找 ” 类型题目

往期内容

< CSS小技巧:filter滤镜妙用>

< JavaScript技术分享: 大文件切片上传 及 断点续传思路 >

< 每日技巧: JavaScript代码优化 >

< 每日知识点:关于Javascript 精进小妙招 ( Js技巧 ) >

你可能感兴趣的:(每日算法,算法,排序算法,数据结构)