二分法模板-leetcode

1、二分法模板(1)

*主要的难点是边界条件的判断,下面一个代码示例是一个最基础的二分法模板,简单题目与部分中等题目比较适合275. H 指数 II - 力扣(LeetCode) (leetcode-cn.com)

class Solution {
    public int searchInsert(int[] nums, int target) {
        int h= nums.length-1;
        int l =0;
     
        {
            int m = l + (h-l)/2;
            if(target <= nums[m])
            h=m;
            else 
            l=m+1;
        }
        return target <= nums[h] ? h:h+1;
    }
}

2、二分法模板(2)

*比较复杂的二分法模板,对应与中等难度的题目,相较于第一个模板,这个模板就比较复杂一点:例如leetcode中的第1482道1482. 制作 m 束花所需的最少天数 - 力扣(LeetCode) (leetcode-cn.com)以及274与275道,需要对mid处的数据进行一个判断,依旧必须考虑边界值。

class Solution {
    public int minDays(int[] bloomDay, int m, int k) {
        if(m*k>bloomDay.length)
        return -1;
        int low = Arrays.stream(bloomDay).min().getAsInt(); 
        int high = Arrays.stream(bloomDay).max().getAsInt(); 
        while(low < high)
        {
            int mid = low+(high-low)/2;
            if(check(bloomDay,m,k,mid))
            high = mid;
            else
            low = mid + 1;
        }
        return  high;
    }
    boolean check(int[] bloomDay,int m,int k,int days)
    {
        int flower = 0;
        int mf = 0;
        for(int i=0;i<bloomDay.length;i++)
        {
            if(bloomDay[i]<=days)
            {
                flower++;
                if(flower==k)
                {
                    mf++;
                    flower =0;
                }
            }
            else flower = 0;
        }
        return mf>=m;
    }
}

 

二分法再学习

1、二分法想起来容易,实现起来缺很复杂,经常会出现边界判断出错的问题。今天我又重新学习了一下34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)这道题目

解题思路:可以分为两部分,第一步寻找左边界,具体做法是当nums[mid] >= target时,右边指针减一,否则左边指针加一,这样如果存在目标值的话,目标值的左边界就是右指针。第二步,寻找右边界,做法与左边界相似,具体解法如下:

public int[] searchRange(int[] nums, int target) {
        int[] ans = new int[]{-1, -1};
        if(nums.length == 0 || nums[0] > target || nums[nums.length - 1] < target)
        return ans;
        int l = 0, r = nums.length - 1;
        //寻找左边界
        while(l <= r){
            int mid = l + (r - l) / 2;
            if(nums[mid] >= target)
            r = mid - 1;
            else
            l = mid + 1; 
        }
        ans[0] = l; 
        
        //寻找右边界
        l = 0; r = nums.length - 1;
        while(l <= r){
            int mid = l + (r - l) / 2;
            if(nums[mid] <= target)
            l = mid + 1;
            else
            r = mid - 1; 
        }
        ans[1] = r;
        if(ans[0] > ans[1])
        return new int[]{-1, -1};
        return ans;
    }

2、153. 寻找旋转排序数组中的最小值 - 力扣(LeetCode)

对于如下图所示的数组,我们要清楚数组的后半部分的最大值比前半部分的最小值要小,换句话说,后半部分小于前半部分,这样的话,我们就可以使用二分法进行判断进行查找了。代码如下:这里需要注意的是,nums[mid] > nums[nums.length - 1]时 l = mid + 1;这是因为此时最小值一定不是mid;而对于nums[mid] < nums[nums.length - 1]的情况就是 mid 有可能是最小值,那么就不能直接 mid - 1,mid - 1 可能会错过最小值。

/*方法一*/
public int findMin(int[] nums) {
        int l = 0, r = nums.length - 1;
        while (l < r){
            int mid = l + (r - l) / 2;
            if(nums[mid] > nums[nums.length - 1])
                l = mid + 1;
            else r = mid;
        }
        return nums[l];
    }
​
/*方法二*/
public int findMin(int[] nums) {
        int l = 0, r = nums.length - 1;
        while (l <= r){
            int mid = l + (r - l) / 2;
            if(nums[mid] > nums[nums.length - 1])
                l = mid + 1;
            else r = mid - 1;
        }
        return nums[l];
    }
​

 

 

3、做完这道还有一道,33. 搜索旋转排序数组 - 力扣(LeetCode)

对与上图这种元素也可以使用二分查找找到最小值,具体做法在代码注释里面有

public static int search(int[] nums, int target) {
        int n = nums.length;
        if(n == 0)
            return -1;
        int l = 0, r = n - 1;
        //利用二分法寻找直接判断,类似于找旋转点
        while(l <= r){
            int mid = l + (r - l) / 2;
            if(nums[mid] == target)
                return mid;
            //中间点位于前半部分
            if(nums[mid] > nums[r]){
                //判断目标值是否位于左端点与中间点之间
                if(nums[mid] > target && target >= nums[l])
                    r = mid - 1;
                else
                    l = mid + 1;
            }
            //中间点位于后半部分
            else{
                //判断目标值是否位于中间点与右端点之间
                if(nums[mid] < target && target <= nums[r])
                    l = mid + 1;
                else
                    r = mid - 1;
            }
        }
        return -1;
    }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值