zoukankan      html  css  js  c++  java
  • [LeetCode] 二分查找模板 binary search

    二分法是算法题里面一个比较基础但是很容易错的概念,一开始练习的时候由于不熟悉二分法的套路,反复出现死循环或者目标值找错,非常影响做题心情。我总结了如下几个模板。原则上这里的模板无论你使用哪一个,都可以解决二分法类型的问题,只不过有一些题目,比如寻找一个最大值/最小值的,可能某一个模板更适合,需要判断的条件较少。

    如下模板是用Java实现的

    模板一,找有序数组中是否存在一个目标值。注意 right 指针一开始定义是在数组下标范围内的,所以while的条件才能写成 <=。

     1 class Solution {
     2     public int binarySearch2(int[] nums, int target) {
     3         // left和right都在数组下标范围内
     4         // [left, right]
     5         int left = 0;
     6         int right = nums.length - 1;
     7         // while循环跳出的条件是left < right
     8         // 所以如果没找到target的话,也不需要特判了
     9         while (left <= right) {
    10             int mid = left + (right - left) / 2;
    11             if (nums[mid] == target) {
    12                 return mid;
    13             } else if (nums[mid] < target) {
    14                 left = mid + 1;
    15             } else {
    16                 right = mid - 1;
    17             }
    18         }
    19         // 如果没找到就只能返回-1
    20         return -1;
    21     }
    22 }

    模板二,适合判断当前 index 和 index + 1 之间的关系。right 指针一开始的定义是在数组下标范围外的,[left, right),所以在需要移动 right 指针的时候不能写成 right = mid - 1。这样会遗漏掉一些下标的判断。

     1 class Solution {
     2     public int binarySearch3(int[] nums, int target) {
     3         // right不在下标范围内
     4         // [left, right)
     5         int left = 0;
     6         int right = nums.length;
     7         // while循环跳出的条件是left == right
     8         // 这个模板比较适合判断当前index和index + 1之间的关系
     9         // left < right, example, left = 0, right = 1
    10         while (left < right) {
    11             int mid = left + (right - left) / 2;
    12             if (nums[mid] == target) {
    13                 return mid;
    14             } else if (nums[mid] < target) {
    15                 left = mid + 1;
    16             } else {
    17                 // 因为搜索范围是左闭右开所以这里不能-1
    18                 right = mid;
    19             }
    20         }
    21         // 最后的特判
    22         if (left != nums.length && nums[left] == target) {
    23             return left;
    24         }
    25         return -1;
    26     }
    27 }

    模板三,适用于查找有序数组中某个元素是否存在。若不存在,往往题目要求返回 -1。注意 right 指针一开始定义是在数组下标范围内的,while条件不满足的时候,left + 1 == right,两下标应该指向某个下标 i 和 i + 1。这样如果有什么特殊的值需要判断,应该不是 left 就是 right 了。

     1 class Solution {
     2     public int binarySearch1(int[] nums, int target) {
     3         // left和right都在数组下标范围内
     4         // [left, right]
     5         int left = 0;
     6         int right = nums.length - 1;
     7         // 举例,start - 0, end = 3
     8         // 中间隔了起码有start + 1和start + 2两个下标
     9         // 这样跳出while循环的时候,start + 1 == end
    10         // 才有了最后的两个判断
    11         while (left + 1 < right) {
    12             int mid = left + (right - left) / 2;
    13             if (nums[mid] == target) {
    14                 return mid;
    15             } else if (nums[mid] < target) {
    16                 left = mid;
    17             } else {
    18                 right = mid;
    19             }
    20         }
    21         // 特判
    22         if (nums[left] == target) {
    23             return left;
    24         }
    25         if (nums[right] == target) {
    26             return right;
    27         }
    28         // 如果没找到就只能返回-1
    29         return -1;
    30     }
    31 }

    如上三个模板的比较如下图

    这 3 个模板的不同之处在于:

    左、中、右索引的分配。
    循环或递归终止条件。
    后处理的必要性。
    模板 #1 和 #3 是最常用的,几乎所有二分查找问题都可以用其中之一轻松实现。模板 #2 更 高级一些,用于解决某些类型的问题。

    这 3 个模板中的每一个都提供了一个特定的用例

    模板 #1 (left <= right)

    二分查找的最基础和最基本的形式。
    查找条件可以在不与元素的两侧进行比较的情况下确定(或使用它周围的特定元素)。
    不需要后处理,因为每一步中,你都在检查是否找到了元素。如果到达末尾,则知道未找到该元素。

    模板 #2 (left < right)

    一种实现二分查找的高级方法。
    查找条件需要访问元素的直接右邻居。
    使用元素的右邻居来确定是否满足条件,并决定是向左还是向右。
    保证查找空间在每一步中至少有 2 个元素。
    需要进行后处理。 当你剩下 1 个元素时,循环 / 递归结束。 需要评估剩余元素是否符合条件。

    模板 #3 (left + 1 < right)

    实现二分查找的另一种方法。
    搜索条件需要访问元素的直接左右邻居。
    使用元素的邻居来确定它是向右还是向左。
    保证查找空间在每个步骤中至少有 3 个元素。
    需要进行后处理。 当剩下 2 个元素时,循环 / 递归结束。 需要评估其余元素是否符合条件。

    作者:力扣 (LeetCode)
    链接:https://leetcode-cn.com/leetbook/read/binary-search/xewjg7/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    LeetCode 题目总结

  • 相关阅读:
    Java程序员:一整个项目的具体开发流程介绍
    JAVA常用API整理
    Java开发人员必知必会的20种常用类库和API
    SpringBoot_web开发【实验】-员工列表-链接高亮&列表完成
    luogu P1754 球迷购票问题 |动态规划
    luogu P1566 加等式 |背包问题方案数
    luogu P1564 膜拜 |动态规划
    luogu P1509 找啊找啊找GF |背包
    P1474 货币系统 Money Systems |背包方案数
    cloudera安装报错 socket.gaierror: [Errno -2] Name or service not known
  • 原文地址:https://www.cnblogs.com/cnoodle/p/14267991.html
Copyright © 2011-2022 走看看