思路
二分查找:
-
利用二分查找找到一个符合条件的值,然后循环搜索这个值前后重复的次数。(循环搜索使得算法一部分退化到O(m))。
时间复杂度O(m+lgn),空间复杂度O(1)。 -
使用两个修改判定的二分查找,分别找到数字第一次出现和最后一次出现的位置。(推荐)
时间复杂度O(lgn),空间复杂度O(1)。 -
由于题目数组的特殊性(int),可以分别对搜索值±0.5,然后使用二分查找搜索两个值。(特殊)
时间复杂度O(lgn),空间复杂度O(1)。
代码1
public class Solution {
public int GetNumberOfK(int [] array , int k) {
if(array == null || array.length == 0) return 0;
int cnt = 0;
int low = 0;
int mid = 0;
int high = array.length - 1;
while(low <= high) {
mid = (low + high) / 2;
if(array[mid] == k) {
break;
} else if(array[mid] < k) {
low = mid + 1;
} else {
high = mid - 1;
}
}
for(int i = mid - 1; i >= 0; i--) {
if(array[i] == k) {
cnt++;
} else {
break;
}
}
for(int i = mid; i < array.length; i++) {
if(array[i] == k) {
cnt++;
} else {
break;
}
}
return cnt;
}
}
代码2
public class Solution {
private int biSearchfirst(int[] arr, int k, int low, int high, int mid) {
while(low <= high) {
mid = (low + high) / 2;
if(arr[mid] == k) {
if(mid == 0 || arr[mid-1]!=k) {
return mid;
} else {
high = mid - 1;
}
} else if(arr[mid] < k) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
}
private int biSearchlast(int[] arr, int k, int low, int high, int mid) {
while(low <= high) {
mid = (low + high) / 2;
if(arr[mid] == k) {
if(mid == arr.length - 1 || arr[mid+1]!=k) {
return mid;
} else {
low = mid + 1;
}
} else if(arr[mid] < k) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return -1;
}
public int GetNumberOfK(int [] array , int k) {
if(array == null || array.length == 0) return 0;
int first = biSearchfirst(array, k, 0, array.length - 1, 0);
int last = biSearchlast(array, k, 0, array.length - 1, 0);
if(first == -1 || last == -1) return 0;
return last - first + 1;
}
}
代码3
public class Solution {
private int biSearch(int[] arr, double k) {
int low = 0;
int mid = 0;
int high = arr.length - 1;
while(low <= high) {
mid = (low + high) / 2;
if(arr[mid] < k) {
low = mid + 1;
} else {
high = mid -1;
}
}
return low;
}
public int GetNumberOfK(int [] array , int k) {
if(array == null || array.length == 0) return 0;
int left = biSearch(array, k - 0.5);
int right = biSearch(array, k + 0.5);
return right - left;
}
}
笔记
二叉查找,while(low <= high),若k存在:
- 此时搜索k-ε的值,无论是否存在,mid返回的都是k的前一位,low返回的是k,high返回的是mid。
- 此时搜索k+ε的值,无论是否存在,mid返回的都是k的后一位,high返回的是k,low返回的是mid。
- 所以代码3里right-left实际上就是low2-low1 = k最后一次出现的后一位 - k第一次出现的位置。
若k不存在:k最后一次出现的后一位 等于 k第一次出现的位置,相当于插进来一个数。
所以二分法使用第三种代码方法时,只需要用low或者high代替mid返回就行,最后位置的逼近自然会完成。
总结,二分法搜索若存在,返回mid,若不存在,则返回low或者high(类比向上向下取整,同时取一个方向的选择)。
二分法与分治法的问题都可以先写出基本框架,再通过修改判定或添加操作解决问题。