二分查找分为通过数组的角标进行查找和通过元素数值进行查找
通过数组的角标进行查找:
通常用于数组数值有序的情况
正常实现:
public int binarySearch(int[] nums, int key) {
int l = 0, h = nums.length - 1;
while (l <= h) {
int m = l + (h - l) / 2;
if (nums[m] == key) {
return m;
} else if (nums[m] > key) {
h = m - 1;
} else {
l = m + 1;
}
}
return -1;
}
时间复杂度: O(logN)。
m 计算:
有两种计算中值 m 的方式:
m = (l + h) / 2
m = l + (h - l) / 2
为避免 l + h 加法溢出,最好使用第二种方式。
返回值:循环退出时如果仍然没有查找到 key,那么表示查找失败。可以有两种返回值:
-1:以一个错误码表示没有查找到 key
l:将 key 插入到 nums 中的正确位置
变种: 二分查找可以有很多变种,变种实现要注意边界值的判断。例如在一个有重复元素的数组中查找 key 的最左位置的实现如下:
public int binarySearch(int[] nums, int key) {
int l = 0, h = nums.length - 1;
while (l < h) {
int m = l + (h - l) / 2;
if (nums[m] >= key) {
h = m;
} else {
l = m + 1;
}
}
return l;
}
该实现和正常实现`有以下不同:
循环条件为 l < h
h 的赋值表达式为 h = m
最后返回 l 而不是 -1
时,不表示没有查找到 key,因此最后返回的结果不应该为 -1。为了验证有没有查找到,需要在调用端判断一下返回位置上的值和 key 是否相等。
参考资料
通过元素数值进行查找
LeetCode应用题目:
378 二维有序矩阵的 Kth Element
287 寻找重复元素(元素数值属于[1,n],,数组为乱序,要求时间复杂度<=O(N))
解题思路:通常需要找到最大最小值,确定mid后,根据mid值找到比mid大或者小的个数来重新界定边界,通常用于数组无序,但数组数值范围已知的情况
287 寻找重复元素
public int findDuplicate(int[] nums) {
int l = 1,r = nums.length-1;
while(l<=r){
int mid = l+(r-l)/2;
int cnt = 0;
for(int i = 0;i<nums.length;i++){
if(nums[i]<=mid)
cnt++;
}
if(cnt>mid)
r = mid-1;
else
l = mid+1;
}
return l;
}
378 有序矩阵的 Kth Element
public int kthSmallest(int[][] matrix, int k) {
int m = matrix.length,n = matrix[0].length;
int l = matrix[0][0],r = matrix[m-1][n-1];
while(l<=r){
int mid = l+(r-l)/2;
int cnt = 0;
for(int i = 0;i<m;i++){
for(int j = 0;j<n;j++){
if(matrix[i][j]<=mid)
cnt++;
}
}
if(cnt<k)
l = mid+1;
else
r = mid-1;
}
return l;
}