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的子数组的最大的平均值;
对于使用二分的两个条件来说,满足随机存储,但是这个数组不是排序的数组,那么对与这道题来说就不是对数组本身进行二分;一个数组的的子数组的平均值必然大于数组元素的最小值,小于数组的最大值,其他的必然存在这个范围内,那么可以对答案进行二分进行查找;