二分查找算法(递归版)
问题描述:
请对一个有序(在这讲的是升序)的数组{1,4,9,11,33,41,69,72,99}进行二分查找,输入一个数看看该数组是否存在该数,并且求出下标,如果没有就返回提示没有这个数。
二分查找思路分析:(对一个有序的数组进行查找)
1.首先确定该数组的中间下标(在这用mid来表示)
mid = (left + right)/2
2.然后让需要查找的数(findVal)与arr[mid]做比较:
1)findVal > arr[mid],说明要查找的数在mid的右边,因此需要递归向右查找
2)findVal < arr[mid],说明要查找的数在mid的左边,因此需要递归向左查找
3)findVal == arr[mid],说明找到,返回
什么时候应该结束递归:
1)找到就结束递归
2)递归完整个数组,仍然没有找到findVal,也需要结束递归(即当left > right)
有人看到这里可能就要疑问了?诶,凭什么left > right,就结束递归了?你说结束就结束是么?别急,看完下面你就明白了(实在不明白的话,可以自己选一个小一点的数组,自己推一遍,我一开始也是一头雾水,后面自己推了一遍过程就很清晰了)
left > right(边界条件):
由题意,在这讲的是升序(降序相反),我们可以知道当寻找一个数时,总要要移动 left 或 right 的下标。
例如:
向左递归时,我们left不变,right等于mid-1(相当于向左移动)
向右递归时,我们right不变,left等于mid+1(相当于向右移动)
只要数组不是无限大的,那么 left 一直向右移动,right 一直向左移动,总有一次当 left > right 时代表所有数都已经找完了,没有找到,这时候就应该停止递归,返回-1,提示我们没有找到这个值了。
代码:
public class BinarySearch {
public static void main(String[] args) {
int[] arr = new int[]{1,4,9,11,33,41,69,72,99};
int index = binarySearch(arr,0,arr.length-1,99);
System.out.printf("index:"+index);
}
public static int binarySearch(int[] arr,int left,int right,int findVal) {
if(left > right){
return -1;//未找到用-1表示
}
int mid = (right + left) / 2;
int midVal = arr[mid];
//找到就返回下标
if(findVal > midVal){//要查找的数在mid的右边,向右递归
return binarySearch(arr,mid +1 ,right,findVal);
}else if (findVal < midVal){//要查找的数在mid的左边,向左递归
return binarySearch(arr,left,mid -1,findVal);
}else {//findVal == arr[mid]
return mid;
}
}
}
>>index:8
扩展:
若数组为{1,49,64,81,666,666,666,1264},当一个有序数组中,有多个相同的数值时如何将所有的数值都查找到,例如这里的元素666。
思路分析:
1.我们用一个ArrayList来接收要找的所有相同数值的索引
2.在找到mid索引值时,不要立刻返回
3.向mid索引值的左边扫描,将所有满足666元素的下标,加入到集合ArrayList中
4.向mid索引值的右边扫描,将所有满足666元素的下标,加入到集合ArrayList中
代码:
public class BinarySearch2 {
public static void main(String[] args) {
int[] arr = new int[]{1,49,64,81,666,666,666,1264};
List<Integer> resIndexList = binarySearch(arr,0,arr.length-1,666);
System.out.printf("resIndexList:" + resIndexList);
}
public static List<Integer> binarySearch(int[] arr,int left,int right,int findVal){
if(left > right){
return new ArrayList<Integer>();
}
int mid = (left + right) / 2;
int midVal = arr[mid];
if(findVal > midVal){
return binarySearch(arr,mid+1,right,findVal);
}else if(findVal < midVal){
return binarySearch(arr,left,mid-1,findVal);
}else {
List<Integer> resIndexList = new ArrayList<Integer>();
//向mid左边扫描
int temp = mid-1;
while (true){
if(temp < 0 || arr[temp] != findVal){ //说明到最左边了,或者前一个数不是要找的目标值
break;
}
//否则就放入进集合中
resIndexList.add(temp);
temp -= 1;
}
resIndexList.add(mid);//将mid放入集合中
temp = mid + 1;
//向mid右边扫描
while (true){
if(temp > arr.length-1 || arr[temp] != findVal){
break;
}
//否则放入集合中
resIndexList.add(temp);
temp += 1;
}
return resIndexList;
}
}
}
>>resIndexList:[4, 5, 6]
二分查找算法(非递归版)
时间复杂度:
二分查找法的运行时间为对数时间O(log2n),即查找到需要的目标位置最多只需要 log2n 步,假设从[0,99]的数列(100个数,即 n = 100)中寻到目标元素30,则需要查找步数为 log2 100,即最多需要查找7次
(2^6 < 100 < 2^7)
思路分析:
同递归的思路一样,唯一不同的就是边界条件变为了left < = rigth
代码:
public static void main(String[] args) {
int[] arr = new int[]{1,4,9,11,33,41,69,72,99};
int index = binarySerch(arr,99);
System.out.printf("index:" + index);
}
public static int binarySerch(int[] arr,int target){
int left = 0;
int right = arr.length - 1;
while(left <= right){//在递归的时候说过,left > right是它的一个边界条件
int mid = (left + right) / 2;
if(target > arr[mid]){
left = mid + 1; //向右递归
}else if (target < arr[mid]){
right = mid - 1; //向左递归
}else {//target == arr[mid]说明找到了
return mid;
}
}
return -1;//-1代表没找到
}
}
二分查找我们就讲到这里,下面我会更新在Leetcode里所归为二分查找类型的算法题型解法: