leetcode刷题之二分法模板总结

这篇文章主要是总结近期在Leetcode简单区刷题遇到的二分法,按照前人经验,继续对二分法的模板进行整理,并加入自己的理解。

模板

模板的使用符合绝大多数的题目,但是具体问题需要对模板进行微调,这些调动也有规律可循,我们先来看看模板长什么样,再根据不同的题目分析这些改动。


image
 public static int searchInsert(int[] nums, int target) {
         int low = 0;
         int high = nums.length;  //1.右边界可以取到
         while(low>>1;  //3.无符号右移,不越界
             if(nums[mid]==target) return mid;
             else if(nums[mid]>target) high = mid -1;
             else low = mid + 1;
         }
         return low;
    }

分析

这篇模板有以下几个需要注意的地方,需要根据题目自行调整,非常简单。
1. 关于mid的计算
以前我习惯写成:mid = (low + high) /2。这样没有考虑int溢出的问题,最好的处理方法为,通过无符号右移一位计算mid,即 mid = (low + high) >>> 1
2.关于循环条件的判断
传统的模板为while(low<=high),然后在循环外return low。这样做的问题是,有时候会忘记到底返回的是low还是high,并且不同题目可能不能做统一的返回。当我们将模板中的循环条件改成while(low 3.关于右边界的取值范围
在传统查找的题目中,右边界的取值有可能为len或者len-1,比如要查找元素插入的位置,插入的位置可能是len。如果只是查找元素所在的位置,那么右边界的取值最大为len-1。我们要根据这些分析,调整high变量的初始值。
4.关于取左中位数or右中位数
我们来看Leetcode69题。

例题1——Leetcode 69

题目大意

实现 int sqrt(int x) 函数。
计算并返回 x 的平方根,其中 x 是非负整数。
由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

示例

输入: 8
输出: 2
说明: 8 的平方根是 2.82842...,
由于返回类型是整数,小数部分将被舍去。

分析

因为返回的是整数,我们可以用二分法完成此题。
1.右边界范围:对于整数x,开平方的解范围应该是[0,x],所以右边界high初始值为x。
2.平方的计算可能为造成int溢出,所以用long存储计算中间值。当然,也可以用Java的Math.sqrt(double a)函数,但是亲测时间性能不高。

代码

class Solution {
    public int mySqrt(int x) {
        long low = 0;
        long high = x;
        while(low < high) {  //1.循环条件不加等号
            long mid = (low + high + 1) >>> 1;  //2.带符号右移  4.调整,取左中位数
            long res = mid * mid;
            if(res == x) return (int)mid;
            else if(res < x) low = mid;  //3.可能造成死循环,一直取右中位数
            else high = mid - 1;
        }
        return (int)low;
    }
} 

注意:对比我们之前的通用模板,此题有些许不同。

  1. else if(res < x) low = mid; 考虑实际情况mid是否可能被取到。
  2. 判断是左收敛还是右边收敛。因为low = mid high = mid -1 ,说明右边收敛,所以mid取右中位数,防止进入死循环。
  3. mid = (low + high +1) >>>1 表示取右中位数。
    mid = (low + high) >>>1 表示取左中位数。

你可能感兴趣的:(leetcode刷题之二分法模板总结)