二分查找-java
- 二分查找
- 基础版
- 改动版
- 平衡版
- 二分查找高级用法
- LeftMost
- 01
- 02
- RightMost
- 01
- 02
- LeftMost和RightMost的方式二的高级用法
二分查找
基础版
arr是待查找升序数组,target是待查找元素
基础版的left和right两个所指向的元素都是待查找值
public static int binarySearchBasic(int[] arr, int target) {int left = 0, right = arr.length - 1; //设置指针和初始值while (left <= right) { //left 到 right 中间有东西int middle = (left + right) / 2;if (target < arr[middle]) { //目标在左边right = middle - 1;} else if (arr[middle] < target) { //目标在右边left = middle + 1;} else { //找到了return middle;}}return -1; //没找到
}
两个改进点:
-
int middle = (left + right) / 2; //改进后,使用无符号右移运算符 int middle = (left + right) >>> 1;
- 如果数组数据过大,(left + right) / 2的计算结果可能就超过int的表示范围,会由正变负
- (left + right) >>> 1,不会出现这种问题
-
返回值为-1,没有什么特殊意义。可以根据返回值算出插入点
- 参考jdk的Arrays工具类
- 返回值改为"-(left + 1)"。left可以表示插入点,这里为了避免索引0位置产生歧义,返回加一之后的相反数
- 插入点计算方法是:“Math.abs(返回值+1)”
改动版
left指向的还是待查找值
right只是作为边界,不指向待查找值
public static int binarySearchAlternative(int[] arr, int target) {int left = 0, right = arr.length; //第一处while(left < right){ //第二处int middle = (left + right) >>> 1;if(target < arr[middle]){right = middle; //第三处}else if(arr[middle] < target){left = middle + 1;}else{return middle;}}return -(left + 1); //如果没有该值,返回该值插入点加一的相反数
}
平衡版
基础版和改动版对于while循环中选择语句的执行次数不平衡。
具体含义是,如果查找的数是不存在的,且当该值小于数组最小值和大于数组最大值的时候执行的比较次数是不一样的
public static int binarySearchBalance(int[] arr, int target) {int left = 0, right = arr.length;while (1 < right - left) { //范围内待查找的元素个数 > 1 时int middle = (left + right) >>> 1;if (target < arr[middle]) { //目标在左边right = middle;} else { //目标在middle 或者 右边left = middle;}}return (target == arr[left]) ? left : -1;
}
二分查找高级用法
LeftMost
01
查找升序数组,某个重复元素最左边的位置的索引。不存在返回-1
public static int binarySearchLeftmost1(int[] arr, int target) {int left = 0, right = arr.length - 1;int candidate = -1;while(left <= right){int middle = (left + right) >>> 1;if(target < arr[middle]){right = middle - 1;}else if(arr[middle] < target){left = middle + 1;}else{//更改候选位置candidate = middle;right = middle - 1;}}return candidate;
}
02
查找升序数组,如果目标值存在目标值索引位置,如果目标值重复存在,返回最左边的索引
如果目标值不存在,返回大于目标值最左边的索引位置
即,返回大于等于目标值的最左边位置的索引
public static int binarySearchLeftmost2(int[] arr, int target) {int left = 0, right = arr.length - 1;while(left <= right){int middle = (left + right) >>> 1;if(target <= arr[middle]){right = middle - 1;}else if(arr[middle] < target){left = middle + 1;}}return left;
}
RightMost
01
查找升序数组,某个重复元素最右边的位置的索引。不存在返回-1
public static int binarySearchRightmost1(int[] arr, int target) {int left = 0, right = arr.length - 1;int candidate = -1;while(left <= right){int middle = (left + right) >>> 1;if(target < arr[middle]){right = middle - 1;}else if(arr[middle] < target){left = middle + 1;}else{//更改候选位置candidate = middle;left = midddle + 1;}}return candidate;
}
02
查找升序数组,如果目标值存在目标值索引位置,如果目标值重复存在,返回最右边的索引
如果目标值不存在,返回小于目标值最右边的索引位置
即,返回小于等于目标值的最右边位置的索引
public static int binarySearchRightmost2(int[] arr, int target) {int left = 0, right = arr.length - 1;while (left <= right) {int middle = (left + right) >>> 1;if (target < arr[middle]) {right = middle - 1;} else {left = middle + 1;}}return left - 1;
}
LeftMost和RightMost的方式二的高级用法
几个名词
范围查询:
基础版leftmost和基础版rightmost应用
- 查询 x < 4 x \lt 4 x<4, 0.. l e f t m o s t ( 4 ) − 1 0 .. leftmost(4) - 1 0..leftmost(4)−1
- 查询 x ≤ 4 x \leq 4 x≤4, 0.. r i g h t m o s t ( 4 ) 0 .. rightmost(4) 0..rightmost(4)
- 查询 4 < x 4 \lt x 4<x,$rightmost(4) + 1 … \infty $
- 查询 4 ≤ x 4 \leq x 4≤x, l e f t m o s t ( 4 ) . . ∞ leftmost(4) .. \infty leftmost(4)..∞
- 查询 4 ≤ x ≤ 7 4 \leq x \leq 7 4≤x≤7, l e f t m o s t ( 4 ) . . r i g h t m o s t ( 7 ) leftmost(4) .. rightmost(7) leftmost(4)..rightmost(7)
- 查询 4 < x < 7 4 \lt x \lt 7 4<x<7, r i g h t m o s t ( 4 ) + 1.. l e f t m o s t ( 7 ) − 1 rightmost(4)+1 .. leftmost(7)-1 rightmost(4)+1..leftmost(7)−1
求排名: l e f t m o s t ( t a r g e t ) + 1 leftmost(target) + 1 leftmost(target)+1
索引从0开始, 排名是从1开始的
- t a r g e t target target 可以不存在,如: l e f t m o s t ( 5 ) + 1 = 6 leftmost(5)+1 = 6 leftmost(5)+1=6
- t a r g e t target target 也可以存在,如: l e f t m o s t ( 4 ) + 1 = 3 leftmost(4)+1 = 3 leftmost(4)+1=3
求前任(predecessor): l e f t m o s t ( t a r g e t ) − 1 leftmost(target) - 1 leftmost(target)−1
前任的索引
- l e f t m o s t ( 3 ) − 1 = 1 leftmost(3) - 1 = 1 leftmost(3)−1=1,前任 a 1 = 2 a_1 = 2 a1=2
- l e f t m o s t ( 4 ) − 1 = 1 leftmost(4) - 1 = 1 leftmost(4)−1=1,前任 a 1 = 2 a_1 = 2 a1=2
求后任(successor): r i g h t m o s t ( t a r g e t ) + 1 rightmost(target)+1 rightmost(target)+1
后任的索引
- r i g h t m o s t ( 5 ) + 1 = 5 rightmost(5) + 1 = 5 rightmost(5)+1=5,后任 a 5 = 7 a_5 = 7 a5=7
- r i g h t m o s t ( 4 ) + 1 = 5 rightmost(4) + 1 = 5 rightmost(4)+1=5,后任 a 5 = 7 a_5 = 7 a5=7
求最近邻居:
- 前任和后任距离更近者