二分法 java

以下假设数组递增。

0、普通二分

目标:从数组中寻找target,有则返回下标,没有则返回-1。

返回值范围:[-1, n-1]

终止while条件:如果不包含target,即正常退出while,则此时必有left=right+1。此时区间[left,right]不包含任何元素,返回-1。

区间缩小规则:

  1. 如果mid==target,返回mid。
  2. 如果mid>target,在[left, mid-1]找,
  3. 否则,在[mid+1,right]找。

特点:如果有多个元素=target,会返回任意一个。

public static int binarySearch(int[] arr, int target){
	int left = 0;
	int right = arr.length-1;
	while(left<=right){// 注意
	    int mid = (left+right)/2;
	    if(arr[mid]==target){
	        return mid;
	    }else if(arr[mid] >target){
	        right = mid-1;
		}else if(arr[mid]<target){
        	left = mid+1;
    	}
	}
	return -1;
}

为什么right初始值是arr.length-1,终止while前有left=right,进而mid=right,如果right=arr.length,会越界。而且根据返回值的意义和范围,left和right需要设置成这样。

1、找第一个大于等于target的下标

目标:找第一个大于等于target的下标;
返回值还有一个含义:小于target的数有几个。
具体来说,如果数组里面有target,返回第一次等于target的元素下标:如果不包含target,则返回第一个大于target 的元素下标。若所有元素均小于target,返回n。

返回值范围:[0,n]

终止while条件:必有left==right。此时区间[left,right]里只有一个元素,直接返回left或right

区间缩小方法:

  1. 如果mid元素大于等于target。要找的必定是当前位置或者当前位置左边,令right=mid,即在[left,mid]之间找。
  2. 如果mid元素小于target,要找的下标必定在右边:left = mid+1,即在[mid+1, right]之间找。
// 只有4个需要注意的地方。
public static int binarySearch(int arr[], int target){
    int left = 0;
    int right = arr.length; // 注意
    while(left<right){ // 注意
        int mid = (left+right)/2;
        if(arr[mid] >= target){// 注意
            right = mid;
        }else{
            left = mid+1;
        }
    }
    return left;// 注意
}

为什么right初始值是arr.length。因为终止while前必有left<right,进而mid<right,所以不用担心越界。而且根据返回值的意义和范围,left和right需要设置成这样。

2、找第一个大于target的下标

目标:找第一个大于target的下标;没有大于target的元素则返回n
返回值还有一个含义:小于等于target的数有几个。

返回值范围:[0, n]

终止while条件:必有left==right。此时区间[left,right]里只有一个元素,直接返回left或right

区间缩小方法:

  1. 如果mid元素大于target。要找的必定是当前位置或当前位置左边,right=mid,即在[left,mid]之间找。
  2. 如果mid元素小于等于target,要找的下标必定在右边:left = mid+1,即在[mid+1, right]之间找。
public static int binarySearch(int arr[], int target){
    int left = 0;
    int right = arr.length;
    while(left<right){
        int mid = (left+right)/2;
        if(arr[mid] > target){// 和上面的代码唯一区别是这里
            right = mid; 
        }else{
            left = mid+1;
        }
    }
    return left;
}

3、找最后一个小于等于target的下标

作用:找出最后一个小于等于target的元素下标。所有元素均大于target则返回-1.

返回值范围:[-1,n-1]

分析:用上面的方法可以找第一个大于target的下标index,因此index-1对应的元素必定不大于target,index-1即为所求。
所以只需要把上面的代码最后一行改成left-1就可以了。

public static int binarySearch(int arr[], int target){
    int left = 0;
    int right = arr.length;
    while(left<right){
        int mid = (left+right)/2;
        if(arr[mid] > target){
            right = mid; 
        }else{
            left = mid+1;
        }
    }
    return left-1;// 注意,和上面代码唯一区别在这里
}

A、找target第一次/最后一次出现的位置

上面可以找出第一个大于等于target的下标index。如果index对应元素等于target,则index则为所求。否则说明不包含target。

同理,上面可以找出最后一个小于等于target的下标index。如果index对应元素等于target,则index则为所求。否则说明不包含target。

注:很多人说除了普通二分,其他while里面是left<right,由此说明其搜索区间是左闭右开,但是我觉得仍然应该理解成左闭右闭。

B、java自带二分法库函数

集合

int Collections.binarySearch(List, target)

从有序集合中搜索target:

  1. 找到则返回下标。(如果有多个target,返回任意一个)
  2. 没找到则返回一个负数index,-index-1 表示比target小的数的数量,即第一个大于target的下标。即target插入数组应该放在哪个位置。

例如在数组 [3,6,6,6,7,10]中找6,返回2;找5,返回-2.说明5应该插在第一个元素。

顺便一提collections集合的主要方法:

sort
reverse
shuffle
fill
swap 交换list中的两个元素
copy(dest, src)
min
max
add addAll
remove removeAll

retainAll
rotate 所有元素右移一段距离
replaceAll  修改oldval 为 newval
indexOfSubList

数组

int Arrays.binarySearch(array, target)

效果同上。

C、总结

例子:
原数组: {3,6,6,9,10,11} 长度n=6

方法返回值范围target=0的返回值target=6的返回值target=15的返回值
普通二分[-1,n-1]-11/2-1
第一个大于等于target[0,n]016
第一个大于target[0,n]036
最后一个小于等于target[-1,n-1]-125
自带库函数binarySearch[-n-1,n-1]-11/2-7

1/2表示可能是1也可能是2.

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值