二分总结

二分法

二分查找算法的基本思想

​ 在一个有序序列中,取中值与目标值做对比,从而选取左区间之一,不断重复取值选区间这个过程,直到找到值或者达到边界条件而退出循环。

由基本思想可以的到二分的两个关键点:

  1. 左右区间的选取(包不包含中值)-- 闭区间还是开区间 [left,mid]/[left,mid)/(mid,right]/[mid,right]
  2. 边界条件的选取 (l<=r或l<r)
    • l<=r 的情况下,循环中的最后一步l==r,此时中值索引是l®,中值与target作比较导致 l+1(退出条件1)或者r-1(退出条件2),从而r<l 退出循环。ps: 此时若关键点1是闭区间包含中值,则有可能导致死循环。
    • l<r的情况下, 最后剩下rl+1, 中值与target做比较 ,由2个推出条件导致 lr 退出循环。

1.查找精确值

在一个有序序列中查找符合要求的精确值。查找值为target的元素下标,不存在则返回-1。

两个关键点的选取:

  1. 左右区间选取时,已判断当前中值不是目标值,故排除中值,是开区间。选取[left,mid)或(mid,right]。
  2. 边界两个都可以
// 这里用的是l<=r,
// 最后 当l=r时,mid=l, 
// nums[mid]>target, r=mid-1,r<l 退出循环
// nums[mid]<target, l = mid+1,l>r 退出循环

// 若用 l<r, 最后一步,当nums[mid]!=target时 l或r移动,l=r,退出循环。 此时的nums[l]是最后答案或是大于target的第一个元素。

public static int  binarySearch(int[] nums, int l, int r, int target){
        while(l<=r){
            int mid = l + (r-l)/2;
            if(nums[mid] == target){
                return mid;  //找到目标值,提前退出循环,返回值
            }else if(nums[mid]>target){          
                //右区间
                r = mid-1;  //mid不可能为答案,去掉mid。
            }else{
                //左区间
                l = mid+1; //mid不可能为答案,去掉mid。
            }
        }
        return -1;
    }

2.左边界(找索引位置)

(1,1,2,2,2,3)这样一个序列找2的最左位置的索引,没有则返回-1。
两个关键点:

  1. 当中值等于target时,因为不确定这个值是否为最左值,排除这个中值,右区间开区间[left,mid),当中值小于或大于target时,也是开区间选取 (mid,right]或[left,mid)。

  2. 由于1选的都是开区间最后 r是位于最左位置的左边,所以用l<=r循环最后一步r==l,用l+1退出循环刚好就是答案
    在这里插入图片描述

    // 找值为target的处于最左位置的索引
        public int leftBound(int[] nums,int l, int r, int target){
            while(l<=r){
                int mid = l + (r-l)/2;
                if(nums[mid]>=target){
                    r = mid-1;  //开区间
                }else{
                    l = mid+1;//开区间
                }
            }
            // 越界或target不存在
            if(l>=nums.length || nums[l] != target)
                return -1;
            return l;
        }
    

    本题的关键点:

    ​ 当中值遇到target的时候,继续取开区间[left,mid)把中值排除,如果排除到(最左位置-1)就锁定了最左边界

    ​ 。不断向左收缩锁定最左边界。

3.右边界(找索引位置)

(1,2,2,2,3,3)这样一个序列找2的最右位置的索引,没有则返回-1。

与左边界同理,不断向右收缩锁定最右边界。

public int leftBound(int[] nums,int l, int r, int target){
        while(l<=r){
            int mid = l + (r-l)/2;
            if(nums[mid]>target){
                r = mid-1;  //开区间
            }else{
                l = mid+1;//开区间
            }
        }
        // 越界或trarget不存在
        if(r<0 || nums[r] != target)
            return -1;
        return r;
    }

4.找大于等于或大于 target的第一个元素(找值)

两个关键点:

  1. 当中值满足题中条件但不确定是否为第一个元素时,没法排除,取闭区间。不满足则可开区间排除

  2. 选取 l<r 时, 最后是r==l,r只在满足条件下移动,l只做排除区间的行为,故 l(r)就是答案.

选取l<=r是,由于不确定l,r谁是第一个,判断大小即可。

public int findNum1(int[] nums, int l, int r, int target){
	while(l < r){
		int mid = (r + l) / 2;
		if(nums[mid] > target){   // 查找大于时不加=,大于等于时 nums[mid] >= target
			r = mid;  //因为找的时大于target的第一个值,nums[mid]右边全排除。
		}else{
			l = mid+1;
			// [left,mid]全部排除
		}
	}
	return nums[r];
}

5.查找小于等于/小于target的最后一个元素(找值)

与4同理

public int findNum1(int[] nums, int l, int r, int target){
	while(l < r){
		int mid = (r + l) / 2;
		if(nums[mid] < target){   // 查找小于时不加=,小于等于时 nums[mid] <= target
			l = mid;  //因为找的时大于target的第一个值,nums[mid]右边全排除。
		}else{
			r = mid+1;
			// [left,mid]全部排除
		}
	}
	return nums[r];
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值