loong - 算法 - 二分查找
1. 定义
二分查找,也叫折半查找,是一种适用于顺序存储结构的查找方法。效率比较高,时间复杂度为O(lgn)。
2. 原理步骤
- 在有序区间(这里暂定升序) arr:[ L , R ] 中获取某个目标值(target)。
- 定义:left = 0;right = arr.length() - 1; mid = ( left + right ) / 2。
- 中间值 arr[mid] 将序列分成左小右大两个子序列,将中间值与目标值(target)比较。
- 若 arr[mid] 大于 target,则向左查找,从 left 到 right = mid - 1 中查找,并执行 步骤3。
- 若 arr[mid] 小于 target,则向右查找,从 left = mid + 1 到 right 中查找,并执行 步骤3。
- 若 arr[mid] 等于 target,则找到了
- 若 left 大于 right 没有找到
3.图解案例
案例一:(可以查到情况)
第一步初始化数据:left = 0; right = 9; mid = 4; target = 8;
arr[mid] < target 向mid右侧查找,left = mid + 1;
第二步:arr[mid] > target 向mid左侧查找,right = mid - 1;
第三步:arr[mid] < target 向有侧查找,left = mid + 1;
第四步:arr[mid] = target 找到了
案例二:查找不到的情况
注意:在第四步 left = right = mid 但 left = mid + 1;会触发递归循环退出条件(left > right)
4.代码实现(JAVA)
递归实现-无重复值优先序列(升序)
/**
* 二分查找
*
* @param arr 有序序列
* @param left 左索引
* @param right 右索引
* @param findVal 查找值
* @return 下标索引
*/
public static int search(int[] arr, int left, int right, int findVal) {
// 当left>right时,说明递归整个数组,但是没有找到
if (left > right) {
return -1;
}
int mid = (left + right) / 2;
int midVal = arr[mid];
if (findVal > midVal) {
return search(arr, mid + 1, right, findVal);
} else if (findVal < midVal) {
return search(arr, 0, midVal - 1, findVal);
} else {
return mid;
}
}
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 8, 9, 10, 11};
System.out.println("运行结果:" + search(arr, 0, arr.length - 1, 9));
}
// 运行结果:7
递归实现-多重复值值有序序列(升序)
/**
* 二分查找
*
* @param arr 有序序列
* @param left 左索引
* @param right 右索引
* @param findVal 查找值
* @return 下标索引
*/
public static ArrayList<Integer> search1(int[] arr, int left, int right, int findVal) {
// 当left>right时,说明递归整个数组,但是没有找到
if (left > right) {
return new ArrayList<>();
}
int mid = (left + right) / 2;
int midVal = arr[mid];
if (findVal > midVal) {
return search1(arr, mid + 1, right, findVal);
} else if (findVal < midVal) {
return search1(arr, 0, midVal - 1, findVal);
} else {
ArrayList<Integer> indexLists = new ArrayList<>();
indexLists.add(mid);
int temp = mid - 1;
// 向左扫描
while (true) {
// 如果索引已经到达最左边,或者对应值不等于查找值,则退出
if (temp < 0 || arr[temp] != findVal) {
break;
}
indexLists.add(temp);
temp--;
}
int temp1 = mid + 1;
while (true) {
if (temp1 > arr.length - 1 || arr[temp1] != findVal) {
break;
}
indexLists.add(temp1);
temp1++;
}
return indexLists;
}
}
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5, 6, 6, 6, 6, 6, 8, 9, 10, 11};
System.out.println("运行结果:" + search1(arr, 0, arr.length - 1, 6));
}
// 运行结果:[6, 5, 7, 8, 9]
非递归实现(升序)
/**
* 二分查找的非递归实现
*
* @param arr 带查找的数组,升序排列
* @param target 需要查找的数
* @return 返回对应下标,-1表示没有找到
*/
public static int binarySearch(int[] arr, int target) {
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (arr[mid] == target) {
return mid;
} else if (arr[mid] > target) {
// 向左边查找
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
5. 优缺点
优点:
- 比较次数少,查找速度快
缺点:
- 待查找序列必须是有序
6. 时间复杂度
算法 | 最优时间复杂度 | 最坏时间复杂度 |
---|---|---|
二分查找 | O(1) | O(logn) |