算法分享:DAY01-二分法(Java细节版)带例题

本文详细解释了二分法中的关键点,包括循环终止条件(left<=rightvsleft<right)、选取middle时的选择,以及左闭右闭区间的重要性。文章还讨论了LeetCode题目的应用和边界处理技巧,以及与移除元素问题中双指针方法的优化。
摘要由CSDN通过智能技术生成

二分法示意图

算法:二分法

学习链接:手把手带你撕出正确的二分法 | 二分查找法 | 二分搜索法 | LeetCode:704. 二分查找_哔哩哔哩_bilibili

相关题目练习:1.leetcode 35题搜索插入35. 搜索插入位置 - 力扣(LeetCode)

2.

二分法题目有2个细节点:

        1.循环终止条件:是left<right ? 还是 left<=right? 

        2.选取middle时:if(nums[middke]>target): right = middle?还是right = middle - 1?   

解决这两个问题的方法:明确循环区间,确定循环不变量

        1,[left,right]:区间定义左闭右闭。那当我们算法中使用时就要坚持左闭右闭的原则

设 left = 0; right = nums-1; while(left ? right)这里的“?”应该是"<"还是"<="?因为双闭区间代表着所有的元素都是待考验的,都有可能是等于target的值,所以应该把left=right的情况考虑进去。比如[0,0]这个数组只有一个数字,所以左右两边都是0,用<来判断就出错了。接着下一步while(left<= right){if(nums[middle]>target){right = middle?还是right = middle - 1? }}  显然是middle-1;理由是:刚才我们探讨了循环区间中下标的意义:因为双闭区间代表着所有的元素都是待考验的。所以middle已经排除,就不要了,所以right = middle。

public class 二分法 {
    //左闭右闭
    public static void main(String[] args){
        int[] nums = new int[]{1,3,4,7,9,11,15,16,47,57};
        int target = 3;
        int ans = erfen(nums,target);
        if(ans ==-1){
            System.out.println("没有目标值"+target);
        }else {
            System.out.println("位置:"+ans+"查找到"+nums[ans]);
        }

    }
    public static int erfen(int[] nums,int target){
        int left =0;
        int right = nums.length-1;
        int middle = 0;
        while (left <= right) {
            middle = (left+right)/2;
            if (nums[middle] > target) {
                right = middle - 1;
            } else if (nums[middle] < target) {
                left = middle + 1;
            } else {
                /*System.out.println("位置:"+middle+"查找到"+nums[middle]);
                break;*/
                return middle;
            }
        }
        return -1;
    }
}

2.[left,right)左闭右开:根据上面的推断,由于最右侧的数据不在考虑范围内,所以当left=right的时候就代表结束了。也可以这么看[1,1)这个数组,这是非法的,因为不能左边取1右边不取1。所以应该是left<right。第二点:while(left<= right){if(nums[middle]>target){right = middle?还是right = middle - 1? }} 因为右边不包含在考虑范围内,若是moddle-1那么就会出错。middle已经确定不是target那么就让middle做新边界。但是left更新的时候要注意是middle+1,和上面一样。

public class 二分法 {
    //左闭右闭
    public static void main(String[] args){
        int[] nums = new int[]{1,3,4,7,9,11,15,16,47,57};
        int target = 3;
        int ans = erfen1(nums,target);
        /*if(ans ==-1){
            System.out.println("没有目标值"+target);
        }else {
            System.out.println("位置:"+ans+"查找到"+nums[ans]);
        }*/
        int ans2 = erfen2(nums,target);
        if(ans ==-1){
            System.out.println("没有目标值"+target);
        }else {
            System.out.println("位置:"+ans2+"查找到"+nums[ans2]);
        }

    }
    public static int erfen1(int[] nums,int target){
        int left =0;
        int right = nums.length-1;
        int middle = 0;
        while (left <= right) {
            middle = (left+right)/2;
            if (nums[middle] > target) {
                right = middle - 1;
            } else if (nums[middle] < target) {
                left = middle + 1;
            } else {
                /*System.out.println("位置:"+middle+"查找到"+nums[middle]);
                break;*/
                return middle;
            }
        }
        return -1;
    }
    public static int erfen2(int[] nums,int target){
        int left =0;
        int right = nums.length-1;
        int middle = 0;
        while (left < right) {
            middle = (left+right)/2;
            if (nums[middle] > target) {
                right = middle ;
            } else if (nums[middle] < target) {
                left = middle + 1;
            } else {
                /*System.out.println("位置:"+middle+"查找到"+nums[middle]);
                break;*/
                return middle;
            }
        }
        return -1;
    }
}

总结:二分法本身并不复杂,它针对有序查找target任务很好用,时间复杂度是O(logN),要注意的是,middle更新的时候为(left+right)/2,这里left和right相加要防止int溢出。还有就是循环边界的考量,得清楚自己的边界设置。


Leetcode35题:主要与二分法区别就是,最后return -1换成如下代码:

if(nums[middle]>target){
            return middle;
        }else{
            return middle+1;
        }

首先二分法用的是左闭右闭,我发现每次循环结束,middle指的位置一定是target的附近(在这个数组中最接近target的数)。所以只需要判定nums[middle]和target的大小,小于middle位置就在middle否则middle+1;


Leetcode27

移除元素(双指针思想)

1.什么时候使用库函数?

        当库函数是你大问题的一小步的时候,使用它;

关于移除元素:暴力的方法显然是:寻找到删除的数字位置,从该个数字后开始依次覆盖。时间复杂度是O(n2)

我们思考一下如何优化?首先删除一个元素找到他是一定的O(n)但是删除它的时候,不一定必须按顺序覆盖:快慢指针可以减少这一次循环 。快慢指针实现代码:思路是1.首先满指针和快指针都等于0;设置一个赋值操作语句(我们期望这块语句可以将不是val的值的快指针覆盖慢指针值),0位置若不是val则原地赋值,若是val,那就不走赋值语句,同时也就不更新slow。

public static  int 双指针(int[] nums,int target){
         int slow =0;
         int fast=0;
         for(fast=0;fast<nums.length;fast++){
             if(nums[fast]!=target){
                 nums[slow] = nums[fast];
                 slow++;
             }
         }
         return slow;

    }

  • 25
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值