Ashun's 技術駅 Ashun's 技術駅
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • JS设计模式总结
  • HTML
  • CSS
  • Vue
  • 现代web布局
  • React
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 技术资源
  • 第一阶段

    • HTML
  • 第二阶段

    • JavaScript
  • 第三阶段

    • Vue
  • 第四阶段

    • 实战项目
  • 每周测试

    • 每周
  • 其他

    • Vue引入UI框架
    • Web前端面试
    • Vue3-resource
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 福利资源
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Ashun

前端界的小学生
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《JavaScript高级程序设计》
    • 《ES6 教程》
    • 《Vue》
    • 《React》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • JS设计模式总结
  • HTML
  • CSS
  • Vue
  • 现代web布局
  • React
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 技术资源
  • 第一阶段

    • HTML
  • 第二阶段

    • JavaScript
  • 第三阶段

    • Vue
  • 第四阶段

    • 实战项目
  • 每周测试

    • 每周
  • 其他

    • Vue引入UI框架
    • Web前端面试
    • Vue3-resource
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 福利资源
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • vue

  • vue3

  • es6

  • JavaScript

  • css

  • webpack

  • http

  • NodeJS

  • React

  • git

  • linux

  • typescript

  • algorithm

    • 01Algorithm
    • 02time_space
    • 03structure
    • 04stack_queue
    • 05Linked List
    • 06set
    • 07tree
    • 08Heap
    • 09graph
    • 10sort
    • 11bubbleSort
    • 12selectionSort
    • 13insertionSort
    • 14mergeSort
    • 15quickSort
    • 16BinarySearch
      • 面试官:说说你对二分查找的理解?如何实现?应用场景?
      • 一、是什么
      • 二、如何实现
      • 三、应用场景
      • 参考文献
    • 17design1
    • 18design2
  • applet

  • design

  • 《Web前端面试》
  • algorithm
xugaoyi
2022-03-25
目录

16BinarySearch

# 面试官:说说你对二分查找的理解?如何实现?应用场景?

# 一、是什么

在计算机科学中,二分查找算法,也称折半搜索算法,是一种在有序数组中查找某一特定元素的搜索算法

想要应用二分查找法,则这一堆数应有如下特性:

  • 存储在数组中
  • 有序排序

搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束

如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较

如果在某一步骤数组为空,则代表找不到

这种搜索算法每一次比较都使搜索范围缩小一半

如下图所示:

相比普通的顺序查找,除了数据量很少的情况下,二分查找会比顺序查找更快,区别如下所示:

# 二、如何实现

基于二分查找的实现,如果数据是有序的,并且不存在重复项,实现代码如下:

function BinarySearch(arr, target) {
    if (arr.length <= 1) return -1
    // 低位下标
    let lowIndex = 0
    // 高位下标
    let highIndex = arr.length - 1

    while (lowIndex <= highIndex) {
        // 中间下标
        const midIndex = Math.floor((lowIndex + highIndex) / 2)
        if (target < arr[midIndex]) {
            highIndex = midIndex - 1
        } else if (target > arr[midIndex]) {
            lowIndex = midIndex + 1
        } else {
            // target === arr[midIndex]
            return midIndex
        }
    }
    return -1
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

如果数组中存在重复项,而我们需要找出第一个制定的值,实现则如下:

function BinarySearchFirst(arr, target) {
    if (arr.length <= 1) return -1
    // 低位下标
    let lowIndex = 0
    // 高位下标
    let highIndex = arr.length - 1

    while (lowIndex <= highIndex) {
        // 中间下标
        const midIndex = Math.floor((lowIndex + highIndex) / 2)
        if (target < arr[midIndex]) {
            highIndex = midIndex - 1
        } else if (target > arr[midIndex]) {
            lowIndex = midIndex + 1
        } else {
            // 当 target 与 arr[midIndex] 相等的时候,如果 midIndex 为0或者前一个数比 target 小那么就找到了第一个等于给定值的元素,直接返回
            if (midIndex === 0 || arr[midIndex - 1] < target) return midIndex
            // 否则高位下标为中间下标减1,继续查找
            highIndex = midIndex - 1
        }
    }
    return -1
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

实际上,除了有序的数组可以使用,还有一种特殊的数组可以应用,那就是轮转后的有序数组

有序数组即一个有序数字以某一个数为轴,将其之前的所有数都轮转到数组的末尾所得

例如,[4, 5, 6, 7, 0, 1, 2]就是一个轮转后的有序数组

该数组的特性是存在一个分界点用来分界两个有序数组,如下:

分界点有如下特性:

  • 分界点元素 >= 第一个元素
  • 分界点元素 < 第一个元素

代码实现如下:

function search (nums, target) {
  // 如果为空或者是空数组的情况
  if (nums == null || !nums.length) {
    return -1;
  }
  // 搜索区间是前闭后闭
  let begin = 0,
    end = nums.length - 1;
  while (begin <= end) {
    // 下面这样写是考虑大数情况下避免溢出
    let mid = begin + ((end - begin) >> 1);
    if (nums[mid] == target) {
      return mid;
    }
    // 如果左边是有序的
    if (nums[begin] <= nums[mid]) {
      //同时target在[ nums[begin],nums[mid] ]中,那么就在这段有序区间查找
      if (nums[begin] <= target && target <= nums[mid]) {
        end = mid - 1;
      } else {
        //否则去反方向查找
        begin = mid + 1;
      }
      //如果右侧是有序的
    } else {
      //同时target在[ nums[mid],nums[end] ]中,那么就在这段有序区间查找
      if (nums[mid] <= target && target <= nums[end]) {
        begin = mid + 1;
      } else {
        end = mid - 1;
      }
    }
  }
  return -1;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

对比普通的二分查找法,为了确定目标数会落在二分后的哪个部分,我们需要更多的判定条件

# 三、应用场景

二分查找法的O(logn)让它成为十分高效的算法。不过它的缺陷却也是比较明显,就在它的限定之上:

  • 有序:我们很难保证我们的数组都是有序的
  • 数组:数组读取效率是O(1),可是它的插入和删除某个元素的效率却是O(n),并且数组的存储是需要连续的内存空间,不适合大数据的情况

关于二分查找的应用场景,主要如下:

  • 不适合数据量太小的数列;数列太小,直接顺序遍历说不定更快,也更简单
  • 每次元素与元素的比较是比较耗时的,这个比较操作耗时占整个遍历算法时间的大部分,那么使用二分查找就能有效减少元素比较的次数
  • 不适合数据量太大的数列,二分查找作用的数据结构是顺序表,也就是数组,数组是需要连续的内存空间的,系统并不一定有这么大的连续内存空间可以使用

# 参考文献

  • https://zh.wikipedia.org/wiki/二分搜尋演算法#javascript_版本 (opens new window)
  • https://www.cnblogs.com/ider/archive/2012/04/01/binary_search.html (opens new window)
编辑 (opens new window)
上次更新: 2023/08/06, 00:38:41
15quickSort
17design1

← 15quickSort 17design1→

最近更新
01
课件-react路由-V6
01-22
02
课件-国际化
01-22
03
课件-redux-toolkit
01-22
更多文章>
Theme by Vdoing | Copyright © 2019-2024 Evan Xu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式