zoukankan      html  css  js  c++  java
  • 二分查找算法

    背景

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

    现在游戏开始,有请小红同学写下一个数字在便利贴上。小红若有期待地在便利贴上写下了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/

  • 相关阅读:
    AES算法,DES算法,RSA算法JAVA实现
    spring官方学习地址
    逐步理解SpringMVC
    sublime前端开发工具常用技巧
    谈谈关键字new
    关于mybatisgenerator的问题
    AOPjdk动态代理的思考
    关于java解析xml文件出现的问题
    Java注解
    git向码云上传代码总结
  • 原文地址:https://www.cnblogs.com/cnroadbridge/p/13345804.html
Copyright © 2011-2022 走看看