1、二分查找使用场景
在有序数组中,快速查找某个数字或者某个范围,最终都是对某个 target的查找,隶属于search算法的一种
2、二分使用要求
1) 必须能够随机存储,O(1)常数时间访问,比如数组或者vector等或者底层依靠顺序存储结构实现的数据结构
2) 必须有序,或者在局部范围内有序,可以进行左右范围的判定
3、时间复杂度
二分的时间复杂为O(logN),递推公式为O(N) = O(N/2) + O(1),通过常熟时间内将原有的问题规模减少到原来的一半;
4、二分的分析步骤
1) 明确对谁使用二分,
2) 二分后如何选择下一个要处理的范围
5、二分经典模版
二分边界采用相邻即退出,然后再去根据题目要求判断先返回end还是先返回start
int binarySearch(vector<int>& nums, int tartget, int start, int end){
int size = nums.size();
if(size == 0 || start > end){
return -1;
}
while(start + 1 < end){
int mid = start + (end - start) / 2;
if(nums[mid] == target){
return mid;
}
else if(nums[mid] > target){
end = mid;
}
else {
start = mid;
}
}
if(nums[start] == target){
return start;
}
if(nums[end] == target){
return end;
}
return -1;
}
二分模版的变形,返回第一个target出现的位置或者返回最后一个target的位置等,就是在内部判断向左向右时,等于的情况怎么办,下面采用例题说明。
6、 二分经典题目
1) 经典二分查找问题
直接使用模版,不解释;
2) 查找目标最后一个位置
给一个升序数组,找到target最后一次出现的位置,如果没出现过返回-1;当中间的数组值等于target的时候,应该将此时的start值更新成mid;
class Solution {
public:
/**
* @param A an integer array sorted in ascending order
* @param target an integer
* @return an integer
*/
int lastPosition(vector<int>& A, int target) {
// Write your code here
int vectorLen = A.size();
if (vectorLen == 0) {
return -1;
}
int start = 0;
int end = vectorLen - 1;
while(start + 1 < end) {
int mid = start + (end - start) / 2;
if(A[mid] == target){
start = mid;
}
else if(A[mid] < target) {
start = mid;
}
else {
end = mid;
}
}
if(A[end] == target) {
return end;
}
if(A[start] == target) {
return start;
}
return -1;
}
};
3) 查找target出现的第一个位置
给定一个排序的整数数组(升序)和一个要查找的整数target
,用O(logn)
的时间查找到target第一次出现的下标(从0开始),如果target不存在于数组中,返回-1
。此时和题目2中的分析类似,当nums[mid] == target时,此时应该更新end的值,因为前面还可能存在target;
class Solution {
public:
/**
* @param nums: The integer array.
* @param target: Target number to find.
* @return: The first position of target. Position starts from 0.
*/
int binarySearch(vector<int> &array, int target) {
// write your code here
int arrayLen = array.size();
if( arrayLen == 0){
return -1;
}
int start = 0;
int end = arrayLen - 1;
while(start + 1 < end){
int mid = start + (end - start) / 2;
if(array[mid] >= target){
end = mid;
}
if(array[mid] < target){
start = mid;
}
}
if(array[start] == target){
return start;
}
if(array[end] == target){
return end;
}
return -1;
}
};
4) 找到排序数组中最接近的元素
在一个排好序的数组 A 中找到 i 使得 A[i] 最接近 target
//还是标准的二分程序,就是在返回值时的判断不一样了,比较谁距离target近
class Solution {
public:
/**
* @param A an integer array sorted in ascending order
* @param target an integer
* @return an integer
*/
int closestNumber(vector<int>& A, int target) {
// Write your code here
int size = A.size();
if(size == 0){
return -1;
}
//标准二分模版定义开始和结束变量
int begin = 0;
int end = size - 1;
while(begin + 1 < end){
int mid = begin + (end - begin) / 2;
if(A[mid] >= target){
end = mid;
}
else {
begin = mid;
}
}
return abs(A[begin] - target) < abs(A[end] - target) ? begin : end;
}
};
5) 最大连续数组的最大平均值
给定一个有正有负的数组,返回长度大于K的子数组的最大的平均值;
对于使用二分的两个条件来说,满足随机存储,但是这个数组不是排序的数组,那么对与这道题来说就不是对数组本身进行二分;一个数组的的子数组的平均值必然大于数组元素的最小值,小于数组的最大值,其他的必然存在这个范围内,那么可以对答案进行二分进行查找;