文章目录
概要
1、查目标值下标
public int search(int[] nums, int target) {
int left=0;
int right = nums.length-1;
while(left<=right) { //要有等于,因为left和right和mid有相等的时候,不能少
int mid=left+(right-left)/2;
if(nums[mid]>target) {
right=mid-1; //若在左边区间,更新右区间,mid不是了,往前,右区间减1
}else if(nums[mid]<target) {
left = mid+1; //若在右边区间,更新左区间,mid不是了,往后找,左区间+1
}else{
return mid; //若mid是输出mid
}
}
return -1;
}
2 、搜索插入位置
public int search(int[] nums, int target) {
int left=0;
int right = nums.length-1;
while(left<=right) { //要有等于,因为left和right和mid有相等的时候,不能少
int mid=left+(right-left)/2;
if(nums[mid]>target) {
right=mid-1; //若在左边区间,更新右区间,mid不是了,往前,右区间减1
}else if(nums[mid]<target) {
left = mid+1; //若在右边区间,更新左区间,mid不是了,往后找,左区间+1
}else{
return mid; //若mid是输出mid
}
}
return mid;
}
3、找右边界
public static int getRightBorder(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
int rightBorder = -1; // 记录一下rightBorder没有被赋值的情况
while (left <= right) {
int middle = left + ((right - left) / 2);
if (nums[middle] > target) {
right = middle - 1;
} else if (nums[middle] < target) {
left = middle + 1;
}else {
left = middle + 1; // 因为要找右边界,先找到了一个,先别急,更新left往后再找找看还有没有相等的。left变大了,mid也变大
rightBorder = middle; //找到了就先把这个右边界给存起来
}
}
return rightBorder;
}
4、找左边界
public static int getLeftBorder(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
int leftBorder = -1; // 记录一下leftBorder没有被赋值的情况
while (left <= right) {
int middle = left + ((right - left) / 2);
if (nums[middle] > target) {
right = middle - 1;
} else if (nums[middle] < target) {
left = middle + 1;
}else {
right = middle - 1; // 因为要找左边界,先找到了一个,先别急,更新right往前再找找看还有没有相等的
leftBorder = middle; //找到了就先把这个左边界给存起来
}
}
return leftBorder ;
}
5、找第一个大于等于目标值的下标
public static int getLeftBorder(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
int leftBorder = -1; // 记录一下leftBorder没有被赋值的情况
while (left <= right) {
int middle = left + ((right - left) / 2);
if (nums[middle] > target) { // 寻找左边界,nums[middle] == target的时候更新right
right = middle - 1;
} else if (nums[middle] < target) {
left = middle + 1;
} else {
right = middle - 1;
leftBorder = middle;
}
}
if (leftBorder == -1) { //和找左边界不同的地方:如果没找到并且下标在数组内,应该返回第一个比目标值大的下标
return left < nums.length ? left : -1;
} else {
return leftBorder;
}
这里和找左边界不同之处是,返回值不同,找左边界没找到返回-1,这里没找到是要返回大于等于目标值的下标,left就是第一个比目标值的下标。left要是在数组内,就返回left,不在就是返回-1.
6、java自带的函数:Arrays类的binarySearch
(1)源码
//两个参数,数组a,和要查找的值key
public static int binarySearch(int[] a, int key) {
return binarySearch0(a, 0, a.length, key);
}
private static int binarySearch0(int[] a, int fromIndex, int toIndex,int key) {
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
int midVal = a[mid];
if (midVal < key)
low = mid + 1;
else if (midVal > key)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found.
}
这里方法的和方法一不一样的地方,是没找到后返回的值是bound: -(low + 1)。
返回结果大于0,就是表示,数组中等于目标值的下标,但注意假如数组中有重复的元素,返回并不是第一个等于的,[1,3,3,5,6],找3,输出的并不是下标1,是2,因为源码中,找到了一个大于等于的值就结束了,并没有接着更改left的值接着往左边找。当然有的时候会输出第一个下标,这要根据重复的元素在数组中的位置,看程序先找到哪一个,可以自己试一下。
返回结果小于0,表示数组没有找到该目标值,那么他应该插入的地方,即为第一个大于目标值的元素下标为**-bound-1**。例子:[1,3,3,5,6],找2,这里的第一个大于目标值的元素是指3,但是是哪个3不知道,而并不是指第一个3。
为什么是这个**-bound-1**,下面结合例子讲
(2)应用:当数组内无重复元素时
public static void main(String[] args) {
int[] arr = {1, 10, 23, 35, 55, 66, 88};
//二分查找,binarySearch方法
int index1 = Arrays.binarySearch(arr,66);
int index2 = Arrays.binarySearch(arr,18);
int index3 = Arrays.binarySearch(arr,-1);
int index4 = Arrays.binarySearch(arr,99);
System.out.println("66的索引值为:" + index1);
System.out.println("18的索引值为:" + index2);
System.out.println("-1的索引值为:" + index3);
System.out.println("99的索引值为:" + index4);
}
输出结果
66的索引为:5
18的索引为:-3
-1的索引为:-1
99的索引为:-8
- key不是数组内的数,但在这个有序数组的范围内:
如在数组{1, 10, 23, 35, 55, 66, 88} 中找18一样:
起始low = 0,high = 6。
第一轮循环:mid = 3,midVal = a[3] = 35 > 18,high = mid - 1 = 2
第二轮循环:mid = 1,midVal = a[1] = 10 < 18,low = mid + 1 = 2
第三轮循环:mid = 2,midVal = a[2] = 23 > 18,high = mid - 1 = 1
第四轮循环:此时low = 2 > high = 1,跳出while循环,返回 bound为- (low +1) = - 3
那么就是第一个比目标值的大的元素下标为-bound-1:2- key不是数组内的数,且小于数组内最小的数:
如在数组{1, 10, 23, 35, 55, 66, 88} 中找-1一样:
由于key = -1 < 1,所以它的low会一直为0,而high和mid会不断向0靠近,最终high = -1时跳出循环,返回的索引就为:- (low +1) = - (0 +1) = -1,这也是为什么返回的是- (low +1).
那么就是第一个比目标值的大的元素下标为-bound-1:0- key不是数组内的数,且大于数组内最大的数:
如在数组{1, 10, 23, 35, 55, 66, 88} 中找99一样:
由于key = 99 > 88,所以它的high会一直为a.length - 1,而low和mid会不断向靠近arr.length - 1,最终low = a.length时跳出循环,返回的索引就为:- (low +1) = - (a.length +1) = - 8
那么就是第一个比目标值的大的元素下标为-bound-1:7
(3)应用:当数组内有重复元素时
这里的重复元素前面已经介绍过,当数组内有重复元素时,[1,3,3,5,6],找3,这个函数找到的这个元素并不是最左边的第一个,因为找到一个就停止了没接着往左边找。
(4)总结,和适用场景
1、假如数组没有重复元素,找到的是第一个下标
2、假如要找的是元素,而不是对应的下标,是否重复不影响