二分法

参考连接

二分查找的基本思想:减而治之

应用「排除法」写对二分查找问题

二分查找、二分边界查找算法的模板代码总结

我作了首诗,保你闭着眼睛也能写对二分查找

二分法可以达到 O(log n)的时间复杂度

一般而言,当一个题目出现以下特性时,你就应该立即联想到它可能需要使用二分查找:

  • 待查找的数组 有序或者部分有序
  • 要求时间复杂度低于O(n),或者直接要求时间复杂度为O(log n)

mid 取值

可以保证两数相加除2永远不会发生 越界问题

还可以保证在查找区间 长度为偶数 时,二分过程中其mid始终指向中间偏左的元素,向下取整

	int mid=left+(right-left)/2;

边界问题

参考链接:

二分法中的边界问题

二分法的边界问题

二分法的边界问题一般和left和right的取值有关,一般可以选择 左闭右闭左闭右开

左闭右闭

left 指向数组中第一个元素(0),right 指向数组中最后一个元素(len-1)

考虑while循环中的条件,因为left和right都在数组范围内,因此 left<=right 也是需要进入循环并判断

循环条件中包含了 left == right的情况,则必须在每次循环中改变 left 和 right的指向,以防止进入死循环

public int search(int[] nums, int target) {

        int left = 0;
        int right = nums.length - 1;
        
        while (left <= right) {
        
        	// 用右移操作替代除法提升性能
            int mid = left + ((right - left) >> 1);
            
            if (nums[mid] == target){
            // 循环终止的条件1 :找到目标值
            	return mid;
            }else if (nums[mid] > target) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
		// 循环终止的条件2 :left > right
        return -1;
    }

左闭右开

情况一

left指向数组中第一个元素(0),right 指向数组的长度(len)

		int left=0;
		int right=arr.length;

        while (left<right){
        	.....
        }

在循环过程中选择 向下取整,使用了左闭右开的方法,向上取整可能出现越界问题

情况2

查找左边界:数组有序,但包含重复元素 或者 数组部分有序,且不包含重复元素

要寻找左边界,搜索范围就需要从右边开始,不断往左边收缩

	public int search(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) {
	                left = mid + 1;
	            } else {
	                right = mid + 1;
	            }
	        }
	        // 当target比nums中所有元素都大时,会存在以下情况使得索引越界
	        if (left >= nums.length || nums[left] != target)
    			return -1;
    			
			return left;
	    }

要寻找左边界


	int left = 0, right = nums.length - 1;
	
    while (left <= right) {
    
        int mid = left + (right - left) / 2;
        
        if (nums[mid] > target) {
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }
    // 检查 right 越界
    if (right < 0 || nums[right] != target)
        return -1;
        
    return right;

情况三

数组部分有序且包含重复元素的

与上面唯一区别就在于对右侧值的收缩更加保守

这种收缩方式可以有效地防止一下子跳过目标边界导致搜索区域的遗漏


	public int search(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) {
	                left = mid + 1;
	            } else if (nums[mid] > target) {
	                right = mid + 1;
	            } else {
	                right--;
	            }
	        }
	        return nums[left] == target ? left : -1;
	    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值