需求:在有序数组A内,查找值 $target$
如果找到返回索引
如果找不到返回 -1
public static int binarySearchBasic(int [] a,int target){
int i = 0;//设置指针和初值
int j = a.length-1;//i~j范围内有东西
while (i <= j) {
int m = (i + j) >>> 1;//无符号右移1位都可以看作除2向下取整,java中除法会自动取整 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:为什么是i<=j意味着区间内有未比较的元素,而不是i>> 1,无符号右移1位都可以看作除2向下取整,java中除法会自动取整 m = (i+j)/2 有问题不会动符号位
问题3:都写成小于号有啥好处?
跟人类思维更一致
*/
问题1:为什么是i<=j意味着区间内有未比较的元素,而不是i
i,j它们指向的元素也会参与比较,因为i,j指向的也有可能是查找目标。
问题2:(i + j) / 2 有没有问题?
最大值相加超出范围会出现负数,同一个二进制数,不把最高位视为符号位,把最高位视为符号位,代表结果不同,java会将最高位视为符号位 (i + j) >>> 1,无符号右移1位都可以看作除2向下取整,java中除法会自动取整 m = (i+j)/2 有问题不会动符号位。
问题3:都写成小于号有啥好处?
跟人类思维更一致。
public static int binarySearchBasic2(int [] a,int target){
int i = 0;
int j = a.length; //第一处 j只是一个边界,它指向的一定不是查找目标,i指向的可能是查找目标
while (i < j) { //第二处 如果查找数组内没有的元素i<=j会陷入死循环,因为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;//没找到
}
二分查找法算法运行语句总次数( floor( log_2(n) )+1)* 5+ 4
public static int linearSearch(int[] a,int target){
for (int i = 0; i < a.length; i++) {
if(a[i] == target){
return i;
}
}
return -1;
}
//1.最差的执行情况
//2.假设每行执行时间一样
//算法运行语句总次数:3*n + 3
线性查找法算法运行语句总次数 3*n + 3
在数据量比较大的情况下二分查找法运行次数比线性查找发低得多
假设已知循环了L次,目标在最左边就会比较L次,目标在最右边就会比较2L次,所以二分查找现在有一个问题就是左边查找元素,右边查找元素并不是平衡的
/**
* 二分查找平衡版
* @param a
* @param target
* @return
*/
public static int binarySearchBasic3(int [] a,int target){
int i = 0;//设置指针和初值
int j = a.length;//i~j范围内有东西
//假设循环了L次 元素在最左边 比较L次 元素在最右边 比较2L次
//二分查找现在一个问题就是左边找元素,右边找元素并不是平衡的
while(1 < j - i){
int m = (i + j) >>> 1;
if(target < a[m]){
j = m;
}else{
i = m;
}
}
if(target == a[i]){
return i;
}else{
return -1;
}
}
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.
}
返回的是-插入点-1
如果要插入
public static void main(String[] args) {
int[] a = {2,4,6,8,9};
int key = 3;
//{0,0,0,0,0,0}
int i = Arrays.binarySearch(a,key);
if(i < 0){
int insertIndex = Math.abs(i + 1);
int[] b = new int[a.length+1];
System.arraycopy(a,0,b,0,insertIndex);
b[insertIndex] = key;
System.arraycopy(a,insertIndex,b,insertIndex+1,a.length-insertIndex);
System.out.println(Arrays.toString(b));
}
}
/**
* 二分查找Leftmost
* @param a
* @param target
* @return
*/
public static int binarySearchBasic4(int [] a,int target){
int i = 0;//设置指针和初值
int j = a.length-1;//i~j范围内有东西
int candidate = -1;//候选者
while (i <= j) {
int m = (i + j) >>> 1;//无符号右移1位都可以看作除2向下取整,java中除法会自动取整 m = (i+j)/2 有问题不会动符号位
if(target < a[m]){//目标在左边
j= m - 1;
}else if(a[m] < target){//目标在右边
i= m + 1;
}else {
//记录候选位置
candidate = m;
j = m - 1;
}
}
return candidate;//没找到
}
/**
* 二分查找Leftmost改动
* @param a
* @param target
* @return
*/
public static int binarySearchBasic6(int [] a,int target){
int i = 0;//设置指针和初值
int j = a.length-1;//i~j范围内有东西
int candidate = -1;//候选者
while (i <= j) {
int m = (i + j) >>> 1;//无符号右移1位都可以看作除2向下取整,java中除法会自动取整 m = (i+j)/2 有问题不会动符号位
if(target <= a[m]){//目标在左边
j= m - 1;
}else{//目标在右边
i= m + 1;
}
}
return i;//返回≥target最靠左索引
}
/**
* 二分查找Rightmost
* @param a
* @param target
* @return
*/
public static int binarySearchBasic5(int [] a,int target){
int i = 0;//设置指针和初值
int j = a.length-1;//i~j范围内有东西
int candidate = -1;//候选者
while (i <= j) {
int m = (i + j) >>> 1;//无符号右移1位都可以看作除2向下取整,java中除法会自动取整 m = (i+j)/2 有问题不会动符号位
if(target < a[m]){//目标在左边
j= m - 1;
}else if(a[m] < target){//目标在右边
i= m + 1;
}else {
//记录候选位置
candidate = m;
i = m + 1;
}
}
return candidate;//没找到
}
/**
* 二分查找Rightmost改动版
* @param a
* @param target
* @return
*/
public static int binarySearchBasic7(int [] a,int target){
int i = 0;//设置指针和初值
int j = a.length-1;//i~j范围内有东西
int candidate = -1;//候选者
while (i <= j) {
int m = (i + j) >>> 1;//无符号右移1位都可以看作除2向下取整,java中除法会自动取整 m = (i+j)/2 有问题不会动符号位
if(target < a[m]){//目标在左边
j= m - 1;
}else {
i = m + 1;
}
}
return i - 1;//返回≤target最靠右索引
}