二分专题leetcode例题

基本思想

首先确定答案在一个区间当中L到R。
通过中点判断答案在区间的某一边。
区间缩小,再次找中点。
70%的二分题目和单调性有关系
95%的题目 存在一种两段性的性质,左边成立,右边不成立。通过这个两段性的性质划分区间。

模板一

最后答案在右区间的左端点。
算mid下取整。

while(l<r){
	int mid = l+r>>1;
	if(check(mid))
		r=mid;
	else
		l=mid+1;
}
return l;

模板二

最后的答案在左区间的右端点。
算mid上取整(避免死循环)

while(l<r){
	int mid = l+r+1>>1;
	if(check(mid)
		l=mid;
	else
		r=mid-1;
}
return l;

流程

  1. 确定二分的边界l,r。定左右区间。
  2. 编写二分的代码框架。
  3. 设计一个check性质,性质要保证答案一定在性质的左右边界处且情况唯一。如果结果在左区间右端点用模板二,如果结果在右区间左端点用模板一
  4. 判断区间如何更新
  5. 如果更新方式写的是l=mid,r=mid-1,就在算mid的时候加一。

例 leetcode 69 X的平方根

按照二分流程分析

  1. 确定二分的边界,答案一定在0-x之间。
  2. 编写二分的代码框架
  3. check性质确定是。mid<=x/mid (防止溢出) 答案为左区间右端点,用模板二。
  4. 判断区间边界的情况,当条件满足时,mid包含在结果区间中。
  5. 更新情况是l=mid,r=mid-1。避免死循环,计算mid时要加一上取整。
    public int mySqrt(int x) {
        int l=0,r=x;
        while(l<r){
            int mid= (l+r+1)>>1;
            if(mid<=x/mid){
                l=mid;
            }else
                r=mid-1;
        }
        return l;
    }

例 leetcode 35 搜索插入位置

这里使用二分模板三

    public int searchInsert(int[] nums, int target) {
        int start=0;
        int end = nums.length-1;
        if(target<nums[0])return 0;
        if(target>nums[nums.length-1])return nums.length;
        while(start+1<end){
            int mid = start+(end-start)/2;
            if(nums[mid]==target) return mid;
            if(nums[mid]<=target){
                start = mid;
            }
            else{
                end=mid;
            }
        }
        if(nums[start]==target) return start;
        if(nums[end]==target) return end;
        return start+1;
      
    }

确定区间
确定条件
确定答案包含在右区间的左端点。使用模板一

        if(nums.length==0||nums[nums.length-1]<target)return nums.length;
        int start=0;
        int end = nums.length-1;
        while(start<end){
            int mid= (start+end)>>1;
            if(nums[mid]<target)
                start=mid+1;
            else
                end=mid;
        }
        return start;
    }

例 leetcode34 排序数组中查找元素的第一个位置和最后一个位置

二分的思想,先找到第一个位置,再找到最后一个位置。
通过两次二分,分别划分两个两段性性质。
分别代入模板一和模板二可以解决。

    int []ans = {-1,-1};
    public int[] searchRange(int[] nums, int target) {
        if(nums.length==0)return ans;
        int l=0,r=nums.length-1;
        while(l<r){
            int mid =(r+l)>>1;
            if(nums[mid]>=target)//右区间左端点
                r=mid;
            else
                l=mid+1;
        }
        if(nums[r]!=target)return ans;
        ans[0]=r;
        l=0;
        r=nums.length-1;
        while(l<r){
            int mid=(r+l+1)>>1;
            if(nums[mid]<=target)//左区间右端点
                l=mid;
             else
                r=mid-1;
        }
        ans[1]=r;
        return ans;
    }

例 leetcode74 搜索二维矩阵

编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。

    public boolean searchMatrix(int[][] matrix, int target) {
        int m =matrix.length;
        if(m==0)return false;
        int n=matrix[0].length;
        if(n==0)return false;
        int start =0,end=m*n-1;
        while(start<end){
            int mid = start+(end-start)/2;
            if(matrix[mid/n][mid%n]>=target)
                end= mid;
            else
                start=mid+1;
        }
        if(matrix[start/n][start%n]==target) return true;
        return false;
    }

例 leetcode153. 寻找旋转排序数组中的最小值

假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
请找出其中最小的元素。
你可以假设数组中不存在重复元素。

    public int findMin(int[] nums) {
        int l = 0,r=nums.length-1;
        while(l<r){
            int mid = l+r>>1;
            if(nums[mid]>=nums[nums.length-1])
                l=mid+1;
            else
                r=mid;
        }
        return nums[r];
    }

例 leetcode 278. 第一个错误的版本

直接二分,注意有样例溢出。

    public int firstBadVersion(int n) {
        int l = 0,r=n;
        while(l<r){
            int mid= l+(r-l)/2;//避免溢出
            if(isBadVersion(mid))
                r=mid;
            else
                l=mid+1;
        }
        return l;
    }

例 leetcode 162. 寻找峰值

这个题简单在于找到任何一个峰值即可。
最朴素的做法是遍历。
这里贴一个二分的代码:

    public int findPeakElement(int[] nums) {
        int l=0,r=nums.length-1;
        while(l<r){
            int mid=l+(r-l)/2;
            if(nums[mid]<nums[mid+1])
                l=mid+1;
            else
                r=mid;
        }
        return l;
    }

例 leetcode275.H指数II

给定一位研究者论文被引用次数的数组(被引用次数是非负整数),数组已经按照升序排列。编写一个方法,计算出研究者的 h 指数。
h 指数的定义: “h 代表“高引用次数”(high citations),一名科研人员的 h 指数是指他(她)的 (N 篇论文中)至多有 h 篇论文分别被引用了至少 h 次。(其余的 N - h 篇论文每篇被引用次数不多于 h 次。)"

    public int hIndex(int[] citations) {
        int n=citations.length;
        if(n==0)return 0;
        int l=0,r=n-1;
        while(l<r){
            int mid =l+(r-l)/2;
            if(citations[mid]>=n-mid)
                r=mid;
            else
                l=mid+1;
        }
        if(citations[l]==0)
            return 0;
        return n-l;
    }

猜你喜欢

转载自blog.csdn.net/tianyouououou/article/details/105151589