二分查找又叫折半查找,时间复杂度与散列表或者二叉查找树一样同为logn。虽然凡是用二分查找能解决的,绝大部分我们更倾向于用散列表或者二叉查找树,但是对于“近似”查找问题,二分查找的优势很明显。以下是一些基于Java语言实现的二分查找相关算法:
/**
* @author lv_ry
* @version 1.0
* @date 2021/12/21 9:03
*/
public class BinerySearchTest {
/**
* 二分查找
*/
public static void main(String[] args) {
int[] test = {1,2,3,4};
System.out.println(test[getTargetIndexByLoop(test, 1)]);
System.out.println(test[getTargetIndexByRecursion(test, 1)]);
int[] test1 = {1,1,2,2,3,4};
System.out.println(test1[findFirstTargetIndex(test1,2)]);
System.out.println(test1[findLastTargetIndex(test1,2)]);
int[] test2 = {1,3,5,7,9};
System.out.println(test2[findFirstGTTargetIndex(test2,5)]);
System.out.println(test2[findLastLTTargetIndex(test2,5)]);
}
/**
* 基于循环(在无重复有序数组中查找指定元素数组下标)
*/
public static int getTargetIndexByLoop(int[] nums, int target){
int left = 0;
int right = nums.length-1;//右下标
while (left <= right){
int middle = left + (right - left >> 1);//为了数值不溢出 位运算比除法更快
if(nums[middle] == target){
//找到了
return middle;
}else if(nums[middle] > target){
//在中间点左边
right = middle - 1;
}else{
//在中间点右边
left = middle + 1;
}
}
//没找到
return -1;
}
/**
* 基于递归(在无重复有序数组中查找指定元素数组下标)
*/
public static int getTargetIndexByRecursion(int[] nums, int target){
return getIndexByRecursion(nums,0,nums.length-1,target);
}
private static int getIndexByRecursion(int[] nums, int left, int right, int target) {
if(left > right) {
return -1;
}
int middle = left + (right - left >> 1);
if(nums[middle] == target){
return middle;
}else if(nums[middle] < target){//在右边
return getIndexByRecursion(nums,middle+1,right,target);
}else{//在左边
return getIndexByRecursion(nums,left,middle-1,target);
}
}
/**
* 二分查找的四种变形问题-1:在可重复有序数组中查找第一个值等于给定元素的数组下标
*/
public static int findFirstTargetIndex(int[] nums, int target){
int left = 0, right = nums.length-1;
while (left <= right){
int middle = left + (right - left >> 1);
if(nums[middle] == target){
//中间位置找到了匹配元素,因为要找第一个,所以看看前面还有没
if(middle == 0 || nums[middle-1] != target){//左边没有匹配的元素
return middle;
}else{//左边还有匹配的元素,即要查找的元素在左边
right = middle - 1;
}
}else if(nums[middle] < target){//在右边
left = middle + 1;
}else{//在左边
right = middle - 1;
}
}
return -1;
}
/**
* 二分查找的四种变形问题-2:在可重复有序数组中查找最后一个值等于给定元素的数组下标
*/
public static int findLastTargetIndex(int[] nums, int target){
int left = 0, right = nums.length-1;
while (left <= right){
int middle = left + (right - left >> 1);
if(nums[middle] == target){
//中间位置找到了匹配元素,因为要找最后一个,所以看看后面还有没
if(middle == nums.length-1 || nums[middle+1] != target){//在右边没有匹配的元素
return middle;
}else{//右边还有匹配的元素,即要查找的元素在右边
left = middle + 1;
}
}else if(nums[middle] < target){//在右边
left = middle + 1;
}else{//在左边
right = middle - 1;
}
}
return -1;
}
/**
* 二分查找的四种变形问题-3:在可重复有序数组中查找第一个值大于给定元素的数组下标
*/
public static int findFirstGTTargetIndex(int[] nums, int target){
int left = 0, right = nums.length-1;
while (left <= right){
int middle = left + (right - left >> 1);
if(nums[middle] > target){
//中间位置找到了匹配元素,因为要找第一个,所以看看前面还有没
if(middle == 0 || nums[middle-1] <= target){//左边没有匹配的元素
return middle;
}else{//前边还有匹配的元素,即要查找的元素在左边
right = middle - 1;
}
}else {//在右边
left = middle + 1;
}
}
return -1;
}
/**
* 二分查找的四种变形问题-3:在可重复有序数组中查找最后一个值小于给定元素的数组下标
*/
public static int findLastLTTargetIndex(int[] nums, int target){
int left = 0, right = nums.length-1;
while (left <= right){
int mid = left + (right -left >> 1);
if(nums[mid] < target){
//找到了小于给定元素的小标,因为要找最后一个,所以看看后面还有没
if(mid == nums.length -1 || nums[mid+1] >= target){
//没有则返回
return mid;
}else{//右半区还有
left = mid + 1;
}
}else {//在左边
right = mid - 1;
}
}
return -1;
}
}