tag数组-刷题预备知识-4.一通百通解决二分查找问题

1. 二分法查找法的基本思想

  • 二分法其实就是使用了分治法的思想;
  • 二分法的基本思想是将n个元素分成大致相等的两部分, 取nums[mid] 与 target 做比较:
    • 如果 target = nums[mid], 则找到target, 运行结束, 返回mid;
    • 如果 target > nums[mid], 则只需要在数组nums的右半部分继续搜索 target即可;
    • 如果 target < nums[mid], 则只需要在数组nums的左半部分继续搜索 target即可;

2. 二分查找的时间复杂度

二分查找法的时间复杂度即为 while循环的次数

总共有n个元素, 不断的折半循环下去就是 n, n/2, n/4,… n/2k (这些都是每次需要进行比较操作的元素个数), 其中k是循环的个数;
请添加图片描述

3. 二分查找的几个模版

一般而言,当一个题目出现以下特征之一时, 我们应该联想到它是否能够使用二分查找解题:

  1. 待查找的数组有序或局部有序
  2. 题目要求我们解题的时间复杂度低于 O(n), 或者直接就要求时间复杂度为O(log n)

二分查找有很多变体, 使用时需要注意查找条件, 判断条件左右边界的更新方式, 三者配合不好就很容易出现死循环或遗漏区间, 这也是因此二分法总被人说"十个二分九个错"的一大原因;

  • 下面我们将介绍几种二分查找的模板:
    • 标准的二分查找
    • 二分查找左边界
    • 二分查找右边届
    • 二分查找左右边界
    • 二分查找极值点

3.1 模板一: 标准的二分查找

标准的二分查找的使用场景(也是算法题目的解题关键字): 数组元素有序且不可重复

我们以经典例题: lt.704-二分查找 为例, 给出这种要求数组元素有序不可重复的二分查找的模板:

//“搜索一个数,如果存在,返回其索引,否则返回 -1

class Solution {
    
    
    public int binarySearch(int[] nums, int target) {
    
    

        //定义每次遍历的左右端点
        int left = 0;
        int right = nums.length - 1;

        //开始遍历
        while(left <= right){
    
    
            //定义中值, 它是判断截取数组前半段还是半段的依据
            int mid = left + (right - left)/2;

            if(nums[mid] == target){
    
    
                return mid;
            }else if(nums[mid] > target){
    
    
                //截取前半段
                right = mid - 1;
            }else if(nums[mid] < target){
    
    
                //截取后半段
                left = mid + 1;
            }
        }

        return -1;
    }
  • 循环条件: left <= right
  • 中间位置计算: mid = left + ((right - left)) >> 1 (位移 >>1 就是 除法运算 /2, 比除法效率稍高一些)
  • 左边界更新: left = mid + 1
  • 右边界更新: right = mid - 1
  • 返回值: mid-1

请添加图片描述

请添加图片描述

为什么推荐使用左闭右闭解二分查找的题目

3.2 模板二: 二分查找边界(左边界, 右边界, 或是左右边界)

  • 整这种一套一个模板的真的浪费精力, 直接一招鲜吧:

laxi的一篇文章

  • 对于二分查找类的问题, 我们统一使用:
//左右边界
int left = 0;
int right = nums.length - 1;

//循环条件
while(left <= right){
    
    
    int mid = left + (right - left) >> 1;
    //边界更新方法根据题目要求
    ...
}

3.2 模板三: 二分查找极值点

  • 二分查找还有一种有趣的变体是二分查找极值点,之前我们使用nums[mid]去比较的时候,常常是和给定的目标值target比,或者和左右边界比较,在二分查找极值点的应用中,我们是和相邻元素去比,以完成某种单调性的检测。关于这一点,我们直接来看一个例子就明白了。

请添加图片描述

  • 这一题的有趣之处在于他要求求一个局部极大值点,并且整个数组不包含重复元素。所以整个数组甚至可以是无序的——你可能很难想象我们可以在一个无序的数组中直接使用二分查找,但是没错!我们确实可以这么干!谁要人家只要一个局部极大值即可呢。
  • 题解:

4. 总结

请添加图片描述

猜你喜欢

转载自blog.csdn.net/nmsLLCSDN/article/details/121397398