lc 69. Sqrt(x) 28:30
lc 35. Search Insert Position 41:36
lc 34. Find First and Last Position of Element in Sorted Array 50:43
lc 74. Search a 2D Matrix 59:48
lc 153. Find Minimum in Rotated Sorted Array 71:35
lc 33. Search in Rotated Sorted Array 86:02
lc 278. First Bad Version 102:17
lc 162. Find Peak Element 106:39
lc 287. Find the Duplicate Number 119:06
lc 275. H-Index II 129:47
https://www.bilibili.com/video/BV1Ft41157zW
二分模板一共有两个,分别适用于不同情况。
算法思路:假设目标值在闭区间[l, r]中, 每次将区间长度缩小一半,当l = r时,我们就找到了目标值。

版本1
当我们将区间[l, r]划分成[l, mid]和[mid + 1, r]时,其更新操作是r = mid或者l = mid + 1;,计算mid时不需要加1。

C++ 代码模板:

  1. int bsearch_1(int l, int r)
  2. {
  3. while (l < r)
  4. {
  5. int mid = l + r >> 1;
  6. if (check(mid)) r = mid;
  7. else l = mid + 1;
  8. }
  9. return l;
  10. }

版本2
当我们将区间[l, r]划分成[l, mid - 1]和[mid, r]时,其更新操作是r = mid - 1或者l = mid;,此时为了防止死循环,计算mid时需要加1。

C++ 代码模板:

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

java一样
image.png
案例一:

69. x 的平方根

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

示例 1:
输入: 4
输出: 2

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

class Solution {
 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 ;
            //check为t方<=x,满足的根号x取下整是左边的最后一个,用模板二,
            //如果mid满足,则根号x要在mid右边,那么l=mid,不满足则,r=mid-1                                      
            else 
                r = mid -1;
            }
            return r;
        }
}

35. 搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。

示例 1:
输入: [1,3,5,6], 5
输出: 2

示例 2:
输入: [1,3,5,6], 2
输出: 1

示例 3:
输入: [1,3,5,6], 7
输出: 4

示例 4:
输入: [1,3,5,6], 0
输出: 0

class Solution {
    public int searchInsert(int[] nums, int target) {
        if(nums.length==0||nums[nums.length-1]<target)//特别判断
        return nums.length;
        int l=0,r=nums.length-1;
        while(l<r){
            int mid =l+r>>1;
            if(nums[mid]>=target) r=mid;
            //确定check为t>=targrt,那么就是右边合法的第一个,就用模板一
            else
                l=mid+1;
        }
        return l;
    }
}

34. 在排序数组中查找元素的第一个和最后一个位置

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]
进阶:

  • 你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?

示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
示例 3:
输入:nums = [], target = 0
输出:[-1,-1]

class Solution {
    public int[] searchRange(int[] nums, int target) {
        if(nums.length==0)
            return new int[]{-1, -1};//判断为空
        int l=0,r=nums.length-1;
        while(l<r){
            int mid =l+r>>1;
            if(nums[mid]>=target) r=mid;
            else l=mid+1;
        }//找出大于等于target的起始位置,对应模板一
        if(nums[r]!=target)
            return new int[]{-1, -1};
        r=nums.length-1;
        int start =l;//从剩下的部分找出小于等于(小于)target的结束位置,对应模板二
         while (l < r) {
            int mid = l + r + 1 >> 1;
            if (nums[mid]<=target) l = mid ;                   
            else 
                r = mid -1;
            }
            int end =r;
            return new int[]{start, end};
        }
}

74. 搜索二维矩阵

编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:

  • 每行中的整数从左到右按升序排列。
  • 每行的第一个整数大于前一行的最后一个整数。


    示例 1:
    二分查找专题 - 图2
    输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
    输出:true

示例 2:
二分查找专题 - 图3
输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13
输出:false

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m=matrix.length,n=matrix[0].length;//二维数组性质,m为行数,n为列数m*n为长度
        int l=0,r=m*n-1;
        while(l<r){
            int mid =l+r>>1;
            if(matrix[mid/n][mid%n]>=target) r=mid;//求出相应位置的值matrix[mid/n][mid%n],因为从0开始,所以mid%n不需要减1
            else l=mid+1;
        }
        if(matrix[r/n][(r%n)]==target)
            return true;
        else
            return false;
    }
}
//下面提供线性扫描的方法,先扫描第一列,找出所在行,然后再判断是否存在
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m=matrix.length,n=matrix[0].length;
        int res=0;//存储最终结果
        for(int i=n-1;i<m*n;i=i+n){//遍历数组的每一行的最后一个元素
            if(matrix[i/n][i%n]>=target&&matrix[i/n][0]<=target){//看这一行的第一个和最后一个是否满足
                int l=i-n+1,r=i;
                while(l<r){
                    int mid =l+r>>1;
                    if(matrix[mid/n][mid%n]>=target) r=mid;//利用二分,模板1找出大于等于的第一个元素
                    else l=mid+1;
                }
                res=l;
            }
        }
        if(matrix[res/n][(res%n)]==target)
                    return true;
        else
                    return false;
    }
}

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

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

示例 1:
输入:nums = [3,4,5,1,2]
输出:1

示例 2:
输入:nums = [4,5,6,7,0,1,2]
输出:0

示例 3:
输入:nums = [1]
输出:1

class Solution {
    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]) r=mid;//利用最后一个元素的关系划分整个数组
            else l=mid+1;
        }
        return nums[l];
    }
}

33. 搜索旋转排序数组

难度中等1150收藏分享切换为英文接收动态反馈
升序排列的整数数组 nums 在预先未知的某个点上进行了旋转(例如, [0,1,2,4,5,6,7] 经旋转后可能变为 [4,5,6,7,0,1,2] )。
请你在数组中搜索 target ,如果数组中存在这个目标值,则返回它的索引,否则返回 -1

示例 1:
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4

示例 2:
输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1
示例 3:
输入:nums = [1], target = 0
输出:-1

class Solution {
    public int search(int[] nums, int target) {
        int l=0,r=nums.length-1;
        while(l<r){
            int mid=l+r>>1;
            if(nums[mid]<=nums[nums.length-1]) r=mid;//利用最后一个元素的关系划分整个数组
            else l=mid+1;
        }
        if(target<=nums[nums.length-1])//增加判断属于哪一段,进行区间选择
            r=nums.length-1;
        else{
            r--;
            l=0;
            }
            while(l<r){
            int mid=l+r>>1;
            if(nums[mid]>=target) r=mid;
            else l=mid+1;
            }
        if(target==nums[l])//r可能为-1,所以用l
            return l;
        else
            return -1;
        }
    }

278. 第一个错误的版本

你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
示例:
给定 n = 5,并且 version = 4 是第一个错误的版本。

调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true

所以,4 是第一个错误的版本。

/* The isBadVersion API is defined in the parent class VersionControl.
      boolean isBadVersion(int version); */

public class Solution extends VersionControl {
    public int firstBadVersion(int n) {
        int l=1,r=n;
        while(l<r){
            int mid=l+(r-l)/2;//对int溢出进行判断
            if(isBadVersion(mid)) r=mid;
            else l=mid+1; 
        }
        return r;
    }
}

162. 寻找峰值

峰值元素是指其值大于左右相邻值的元素。
给你一个输入数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞

示例 1:
输入:nums = [1,2,3,1]
输出:2
解释:3 是峰值元素,你的函数应该返回其索引 2。
示例 2:
输入:nums = [1,2,1,3,5,6,4]
输出:1 或 5
解释:你的函数可以返回索引 1,其峰值元素为 2;
或者返回索引 5, 其峰值元素为 6。

class Solution {
    public int findPeakElement(int[] nums) {
        int l=0,r=nums.length-1;
        while(l<r){
            int mid =l+r>>1;
            if(nums[mid]>nums[mid+1]) r=mid;//mid+1可能存在边界问题,因为while保证l!=r,所以不存在l=r=n-1
            else l=mid+1;
        }
        return l;
    }
}

287. 寻找重复数

给定一个包含 n + 1 个整数的数组 nums ,其数字都在 1n 之间(包括 1n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,找出 这个重复的数

示例 1:
输入:nums = [1,3,4,2,2]
输出:2

示例 2:
输入:nums = [3,1,3,4,2]
输出:3

示例 3:
输入:nums = [1,1]
输出:1

示例 4:
输入:nums = [1,1,2]
输出:1

class Solution {
    public int findDuplicate(int[] nums) {
        int l=1,r=nums.length-1;
        while(l<r){
            int mid =l+r>>1;
            int cnt=0;
            for(int x:nums)//遍历数组看是否
                if(x>=l&&x<=mid)
                    cnt++;
            if(cnt>mid-l+1) r=mid;
            else l=mid+1;
        }
        return r;
    }
}
class Solution {
    public int findDuplicate(int[] nums) {
        int[] myList = new int[nums.length];//hash的思想
        for(int i=0;i<nums.length;i++){
            myList[nums[i]]++;
            if(myList[nums[i]]==2)
            return nums[i];
        }
        return 0;
    }
}

275. H 指数 II

难度中等82收藏分享切换为英文接收动态反馈
给定一位研究者论文被引用次数的数组(被引用次数是非负整数),数组已经按照 升序排列 。编写一个方法,计算出研究者的 h 指数。
h 指数的定义: “h 代表“高引用次数”(high citations),一名科研人员的 h 指数是指他(她)的 (N 篇论文中)总共有 h 篇论文分别被引用了至少 h 次。(其余的 N - h 篇论文每篇被引用次数不多于 h 次。)”

示例:
输入: citations = [0,1,3,5,6]
输出: 3
解释: 给定数组表示研究者总共有 5 篇论文,每篇论文相应的被引用了 0, 1, 3, 5, 6 次。
由于研究者有 3篇论文每篇至少被引用了 3 次,其余两篇论文每篇被引用不多于 3 次,所以她的 h 指数是 3

class Solution {
    public int hIndex(int[] citations) {
        int l=0,r=citations.length;
        while(l<r){
            int mid=l+r>>1;
            if(citations[citations.length-mid]>mid) l=mid;
            //至少存在h个数>=h,check为倒数第h大于等于mid
            else r=mid-1;
        }
        return citations[r];

    }
}