• 二分查找算法


    背景

    今天,小郑和小明还有小红在玩一个猜数字的游戏,小红做庄。游戏的规则是参赛选手每人三次机会,如果参赛选手用完三次机会后,还没有猜中数字,他们就要请做庄的人吃棒棒糖,如果他们猜中了,坐庄的人请他们吃棒棒糖。

    现在游戏开始,有请小红同学写下一个数字在便利贴上。小红若有期待地在便利贴上写下了97

    小明:50,对吗?

    小红:不对, 偏小

    小郑:75, 可还行?

    小红:哈哈,错啦,偏小

    小明:87,对吗?

    小红:你又错了,偏小

    小郑:93, 对吗?

    小红:差一点哦,偏小

    小明:是不是96?

    小红:好可惜啊,你没有机会了,请吃棒棒糖吧。

    小郑若有所思地回答:97

    小红:哇,你好棒啊,走我们去吃棒棒糖吧。

    从楼上这个例子,我们可以抽出一个模型。给定一组有序排列的数组,每次将目标值和靠近范围中间的值进行比较,如果偏大就把中间值给左边范围再从新的范围中间值找,如果偏小就把中间值给右边范围再从新的范围中间值找,如果找到了我们返回其数组下标对应的索引,如果没有找到我们就返回-1。形如这样的一种查找方法,我们将其称之为“二分查找”。

    实现一个二分查找算法

    leetcode上有一题关于二分查找的题目,我们就以这个为例来实现一个二分查找。

    题目描述

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

    示例 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

    示例3

    输入:nums=[2, 5], target=2

    输出:0,

    解释:2出现在nums中并且下标为0

    提示:

    你可以假设 nums 中的所有元素是不重复的。
    n 将在 [1, 10000]之间。
    nums 的每个元素都将在 [-9999, 9999]之间。

    这里的提示对审题很有帮助,第一句话,它告诉你叫你放心这些数据都是不重复的,你放心写最简单那种写法,第二句话,告诉你撑死也就10000个数字,数据量还算好的,第三句话,告诉你不用担心整型的范围会溢出、而且这里是有符号的整型。所以提示还是很有帮助的,虽然不一定能够体现在代码上。

    思路

    我们先分析下二分查找干了件什么事?无非就是在一个范围内取中间那位和目标元素进行比大小,如果没有恰好等于目标元素,我就继续选择两段其中的一段继续劈它,直到劈到还剩下最后一个元素。所以显而易见的我们需要一个while循环来帮助我们做这样一件事。第二件事我们就要思考了,这个while循环成立的条件是什么?总不能搞个死循环吧。当左边标志位的小于等于右边标志位的时候,我们继续循环。这里小于大家可能很好理解,那么等于是什么情况呢。我们就举一举示例3的例子,当我想要找到5这个数的时候,第一次left=mid=0, right=1,这个时候我们发现偏小,然后如果不取等号的情况下,我们就退出啦,所以这里加上等号,第二次left=right=1的时候,刚好找到它。紧接着我们思考第三件事情,我取哪一半呢?这个时候就需要对于中间那位和目标那位两者的大小了,如果偏大我就把中间那位减去1赋值给right,如果偏小,那么我就把中间那位加1赋值给left,然后重新计算中间那位再次进行比较。好,接下来我们一起码一码。

    代码实现

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

    问题思考

    二分查找的时间复杂度是多少?

    先说答案,O(logn), 大致的推到流程是,n(1/2)^k = 1, 倒推下k = log2n, 反应到计算机上的时间复杂度就是logn

    二分查找适用的场景是什么?

    面试刷人(因为容易写错),数据量中等,且数据不溢出范围,最重要的是一组排好序的数进行二分查找。

    就拿我们上面最开头的例子讲,普通的查找要97次,而用了二分查找的思想6次了,这不是很香嘛。

    参考文献

    704.二分查找(leetcode): https://leetcode-cn.com/problems/binary-search/

  • 相关阅读:
    61. 最长不含重复字符的子字符串
    60. 礼物的最大价值 (未理解)
    59. 把数字翻译成字符串
    58. 把数组排成最小的数
    57. 数字序列中某一位的数字 (不懂)
    spring data jpa 官方文档
    idea 编译报错 源发行版 1.8 需要目标发行版 1.8
    idea maven 依赖报错 invalid classes root
    solr
    spring boot 官方文档
  • 原文地址:https://www.cnblogs.com/cnroadbridge/p/13345804.html
Copyright © 2020-2023  润新知