二分查找总结与最佳实践

(一) 使用场景

  1. 通常在有序的数字中,找到某个满足一定条件的数
  2. 多次问询题
  3. 需要多次遍历且数据量极大

(二)基本流程

  1. 确定边界(low, high)值
  2. 确定边界移动条件,一定只写一边移动(low=mid high=mid-1) 或者 (low=mid+1 high=mid)
  3. 循环外返回结果 (划重点)

(三)简单例题

给定一个有序数组nums和一个整数target,判断target是否存在于nums中?

 public boolean binary_search(int[] nums , int target) { 
        int low = 0;
        int high = nums.length-1;
        while(low<high){
            int mid = low + (high-low)/2;    //标准写法,避免int相加越界
            if(nums[mid]>target) low = mid+1;
            else if(nums[mid]<target]) high = mid;
        }
        return nums[low]==target;  //循环外返回结果
    }

(四)精选例题

(1)Leetcode 875 KoKo Eating Bananas

Koko喜欢吃香蕉,现存在N盘香蕉,每盘上有piles[i]根香蕉,总共有H个小时可以让koko进食
Koko每小时可以吃K根香蕉,但是同一小时只能选择吃一盘香蕉。
计算最小的K值,使得H小时内koko能将所有香蕉吃完。

输入: piles = [3, 6, 7, 8], H = 8
输出: K = 4

public class Eating_Bananas_875 {
    public int minEatingSpeed(int[] piles, int H) {
        int low = 1 , high = piles[0];
        for(int i=1;i<piles.length;i++){
            if(piles[i]>high) high = piles[i];
        }
        while(low<high){ 
            int mid = low + (high-low)/2;
            if(check(piles,mid,H)) high = mid;
            else low = mid+1;
        }
        return low;//循环外返回
    }
    
    public boolean check(int[] piles , int speed,int H){  //最优解是low与high相遇的时候才是
        int time = 0;
        for(int i=0;i<piles.length;i++){
            if(piles[i]%speed==0) time+=piles[i]/speed;  //每次吃一盘后剩余时间不能去吃其他盘
            else time += piles[i]/speed+1;
        }
        return time<=H;
    }
}

(2) Leetcode 878 Nth Magical Number

定义一个整数是神奇的,当且仅当这个能被A或者B整除
给定A,B,返回第N个神奇数字,答案可能非常大,需要mod 10^9+7
1<=N<=10^9, 2<=A,B<=40000

输入: N = 4,A = 2,B = 3
输出: 6
解释:2,3,4,6

public class Nth_Magical_Number_878 {
    public int nthMagicalNumber(int N, int A, int B) {
        long low=2L,high=100000000000000L; //上界到底怎么算呢?随便找个最大值试一下
        while(low<high){
            long mid = low + (high-low)/2;
            if(check(mid,N,A,B)) low = mid+1;
            else high = mid;
        }
        return (int)(low%1000000007); //少加了括号导致WA了
    }
    
    public boolean check(long mid,int N,int A,int B){
        long magicals = mid/A + mid/B - mid/lcm(A,B);
        return magicals<N ;
    }
    
    public int gcd(int a,int b){ //最大公约数
        return b==0?a:gcd(b,a%b);
    }
    
    public int lcm(int a,int b){ //最小公倍数
        return a*b/gcd(a,b);
    }
}

(三) Leetcode 778 Swim in Rising Water

给定一个NxN的网格grid,请你找到一条道路,从(0,0)出发一直走到(n-1,n-1),使得这条路上的最大值最小。并返回这个值。
输入输出
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_44038165/article/details/88399981