7.3 学习总结二分模板

文章介绍了二分查找的基本思想和模板,包括在有序数组中查找特定数值、寻找最小值和最大值的情况。通过示例代码展示了二分查找在LeetCode题目中的应用,如包裹运输和香蕉食用速度问题,强调了在不同场景中调整搜索边界的重要性。
摘要由CSDN通过智能技术生成

今天重要的知识点是看了二分查找,需要掌握的是正确的发现题目中的二分查找的思想。

二分查找的用到的地方  判断有序数组中是否存在某一个数字  求最小值就是左查询,求最大值右查询。

基本模板

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)
            left = mid + 1; // 注意
        else if (nums[mid] > target)
            right = mid - 1; // 注意
    }
    return -1;
}

int left_bound(int[] nums, int target) {
    if (nums.length == 0) return -1;
    int left = 0, right = nums.length;
    
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            // 当找到 target 时,收缩右侧边界
            right = mid;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid;
        }
    }
    return left;
}

 经典的搜索左边界的问题, 找到相的时候,right=mid,继续向左进行搜索。

简单花间一下 就是 nums[mid]>=target的时候right=mid,向左进行搜索。同理向右进行搜索不再展示出来。

我自己的模板

public class test {
    public static void main(String[] args) {
        int arr[] = {3, 6, 7, 11};
      int left=0,right=arr.length;
        Scanner scanner =new Scanner(System.in);
        int target=scanner.nextInt();
      //这是左闭右开的场景
        //寻找右侧的
      while (left<right){
          int mid=left+(right-left)/2;
          if (arr[mid]<=target){
            left=mid+1;
          }else {
           right=mid;
          }
      }
      if (left<0||left>arr.length){
          System.out.println(-1);
          System.exit(0);
      }
        System.out.println(left-1);
    }

}

二分经典题目

leetcode 1011 

在传送带上面包裹必须在days天内从一个港口运送到另一个港口,
给出包裹的质量,包裹是依次运出的,给出天数,在这些天内都运出去,
返回days天内将传送带上所有包裹去拿不运出去的最低能力
class Solution {
    public int shipWithinDays(int[] weights, int days) {

        //运输 涉及到赵左边的最小值或者右边的最大值问题,都可以采用二分查找来解决
        //二分 letf 和right是如何确定的呢?
        //days天运出,最多一天,最迟每天云一趟,那么最大承载量就是所有的和
        // 最小的承载力就是最大的包裹的质量
        int left=Arrays.stream(weights).max().getAsInt();
        int right=Arrays.stream(weights).sum(); //求得和

        while (left<right){
            //往左搜索
            int mid =left+(right-left)/2;

            if (isC(weights,days,mid)) {
                //运的比较多了,我们要往小的进行寻找
                right=mid;
            }else{
                //每天云的太少了
                left=mid+1;
            }
        }
          //不断的往左进行搜索的,结束的条件是left=right
        return  right;

    }
    public  static boolean isC(int weights[],int Days,int H){

        //如何判断呢
        int count=1; //记录当前的天数
      int leiji=0;
        for (int i = 0; i < weights.length; i++) {
            leiji+=weights[i];

            if (leiji>H){
                count++;
                leiji=weights[i];
            }
            if (count>Days){
                return false;//运载量是现实太小了根部运输不了
            }
        }
        return true;
    }
}

leetcode 875

有n堆香蕉,存在数组里面了,i下标对应的索引就是多少跟香蕉,警卫离开了,将在h小时后回来。

珂珂决定他吃香蕉的速度,一个小时吃掉k跟,如果这堆香蕉小于k跟,那么就直接吃掉,现在求h小时内吃掉所有香蕉的最小速度

//你可以一个小时内吃掉所有的香蕉,也可以旋转慢慢吃香蕉

确定边界 left是左边界,每个小时必须吃1个

right是右边界,每个小时最多全部吃完

这是第一版写的答案,没有处理好时间关系
int left=1;//不太好确定左边界
        int right= Arrays.stream(piles).max().getAsInt();
        //因为一个小时吃完,那么最快就吃一堆
        //向左进行搜索的
        while(left<right){
            int mid=left+(right-left)/2;
            if (isS(piles,h,mid)){
                //符合的话,是向左进行逼近的
                right=mid; //不断的向左进行收缩 这个速度是可以直接吃完的
            }else {
                left=mid+1;
            }
        }
        return left;
    }
    public  static  boolean isS(int []piles,int h,int speed){
        //如何判断是否满足呢?

        //每一个小时吃一推,多了就不吃了
        int time=1;
        for (int i = 0; i < piles.length; i++) {
            int temp=0;
            //这是这一推的东西
            time++;
            temp=piles[i]-speed;

            while (piles[i]>speed) {
                time++;
                //ear>s,ear-s第一次吃完,ear-s-s第二次吃完,
                //下一次就再减去
                temp=temp-speed;
                if (temp<=0){
                    break;
                }
            }
            //时间边了,就返回false
            if (time>h){
                return false;
            }
        }
        return true;
    }

//正确处理时间关系
class Solution {
    public int minEatingSpeed(int[] piles, int h) {
        int right=Arrays.stream(piles).max().getAsInt();
        int left=1;
        while (left<right){
            int mid =left+(right-left)/2;
            int costTime=0;
            for (int pile:piles){
                int currentTime=pile/mid;

                if (mid*currentTime<pile){
                    currentTime++;
                }
              costTime+=currentTime;
            }
         if (costTime<=h){
             //比给定的时间小,要继续向左搜索
             right=mid;
         }else{
             left=mid+1;
             //说明比时间大,速度要加快了
         }
        }
        return left;
    }
}

leetcode 410分割数组的最大值

本子都是向左进行搜索的

 

class Solution {
    public int splitArray(int[] nums, int k) {
         int right= Arrays.stream(nums).sum();
         int left=right/k;
         //先将连个数组进行分割,找出他们的最大值,但是要求最大值里面的最小值,一眼就是向右搜索
         //right 将数组分为一组 。left平均分为k组最大值
        // 因为你你不管怎么分,最大值一定是大于平均值的
        while (left<right){
            int mid =left+(right-left)/2;
            if (check(nums,k,mid)){
                //向左探索的
                right=mid;
            }else {
                left=mid+1;
            }
        }
        return left;
    }
    public static  boolean check(int []nums,int m,int sum){
   int part=1; //初始为一个子数组
        int tempsum=0;
        for (int num:nums){
            if (num>sum){
                return false; //如果硬件有一个数比要求的sum大了,直接返回false
            }
            if (tempsum+num>sum){ //一个子数组放不下了
                 part++;
                 if (part>m){
                     return false;
                 }
                 tempsum=num;

            }else{
                tempsum+=num;
            }
        }
        return  part<=m;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

全栈阿星

您的支持是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值