二分查找适用于升序数组,不然达不到其查找效果
二分查找基础版代码实现如下:
public static int binarySearchBasic(int[] a, int target) {
int i = 0;
int j = a.length - 1; //设置两个指针,一个指向第一个元素,一个指向最后一个元素
while (i <= j) { //循环进行的条件:i~j之间存在元素
int m = (i + j) / 2;
if (target < a[m]) { //目标元素在左边
j = m - 1;
} else if (a[m] < target) { //目标元素在左边
i = m + 1;
} else {
return m; //找到了目标元素,返回其下标
}
}
return -1; //没找到,返回-1
}
问题1:为什么 i <= j 意味着区间内有未比较的元素,而不是 i < j ?
因为 i 和 j 它们指向的元素也会参与比较
问题2: m = ( i + j ) / 2 有没有问题 ?
有问题,如果这个数组很大,( i + j ) 超出了 int 的范围,( i + j ) / 2 为负数(在Java中,把二进制最高位看作符号位,0代表正数。1代表负数)
可以采用无符号右移运算符:>>>
二分查找改动版代码实现如下:
public static int binarySearchBasic(int[] a, int target) {
int i = 0;
int j = a.length;
while (i < j) {
int m = (i + j) >>> 1;
if (target < a[m]) {
j = m ;
} else if (a[m]<target){
i = m + 1;
}else {
return m;
}
}
return -1;
}
改动版的二分查找把 j 的定义进行了调整,j 改动后作为边界,并不会参与比较,但 i 的定义不变
当数组中有重复元素,想通过二分查找返回最小的索引
代码实现:
public static int binarySearchLeftmost(int[] a, int target) {
int i = 0;
int j = a.length - 1;
int index = -1; //设置一个索引变量来记录待查找元素索引
while (i <= j) {
int m = (i + j) >>> 1;
if (target < a[m]) {
j = m - 1;
} else if (a[m] < target) {
i = m + 1;
} else {
index = m; //找到了待查找元素,先把元素索引记录下来
j = m - 1; //再向左边查找,看看有没有待查找元素
}
}
return index;
}
binarySearchLeftmost 改动版:
public static int binarySearchLeftmost(int[] a, int target) {
int i = 0;
int j = a.length - 1;
while (i <= j) {
int m = (i + j) >>> 1;
if (target <= a[m]) { //待查找元素小于等于中间元素都继续向左找
j = m - 1;
} else {
i = m + 1;
}
}
return i; //如果待查找元素存在,返回的是重复元素中最靠左的索引
//如果待查找元素不存在,返回的是大于重复元素且最靠左的索引
}
当数组中有重复元素,想通过二分查找返回最大返的索引
代码实现:
public static int binarySearchRightmost(int[] a, int target) {
int i = 0;
int j = a.length - 1;
while (i <= j) {
int m = (i + j) >>> 1;
if (target < a[m]) {
j = m - 1;
} else { //待查找元素大于等于中间元素都继续向左找
i = m + 1;
}
}
return i-1; //如果待查找元素存在,返回的是重复元素中最靠右的索引
//如果待查找元素不存在,返回的是小于重复元素且最靠右的索引
}