算法模板-----二分查找
Posted 栋次大次
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法模板-----二分查找相关的知识,希望对你有一定的参考价值。
二分查找
find-first-and-last-position-of-element-in-sorted-array
search-insert-position
search-a-2d-matrix
first-bad-version
find-minimum-in-rotated-sorted-array
find-minimum-in-rotated-sorted-array-ii
search-in-rotated-sorted-array
search-in-rotated-sorted-array-ii
模板
给定一个有序数组和目标值,找到第一次、最后一次、任意一次出现的索引,如果没有出现返回-1.
二分查找四个要点:
- 初始化:start=0,end=len-1;
- 循环退出条件:start+1<end;
- 比较中点和目标值;
- 判断最后两个元素是否符合: A[start]、A[end] ? target.
时间复杂度是O(logn),在有序数组的查找比较常用。
给定一个有序整数数组nums和一个目标值target,搜索target,若存在返回其下标,不存在返回-1。
// 常用模板
int search(vector<int>& nums, int target)
int start = 0, end = nums.size()-1;
while(start + 1 < end)
int mid = start + (end - start) /2;
if(nums[mid] == target)
end = mid;
else if(nums[mid] < target)
start = mid;
else
end = mid;
if(nums[start] == target) return start;
if(nums[end] == target) return end;
return -1;
遇到实际问题,对这个模板进行适当修改,大部分题目都可使用。下面有一些其他模板,大部分场景使用模板三,而且还能找到第一次/最后一次出现的位置。
常见题型
find-first-and-last-position-of-element-in-sorted-array
给定一个按照升序排列的整数数组和一个目标值,找出目标值在数组中的开始位置和结束位置。
思路:用两次二分查找分别找第一次和最后一次的位置索引
vector<int> searchRange(vector<int>& nums, int target)
vector<int> res-1, -1;
if(nums.empty()) return res;
int begin = 0, end = nums.size()-1;
while(begin + 1 < end)
int mid = begin + (end - begin) / 2;
if(nums[mid] < target)
begin = mid;
else if(nums[mid] > target)
end = mid;
else
// 如果相等,继续向左找,能找到第一个目标值的位置
end = mid;
// 搜索左边的索引
if(nums[begin] == target)
res[0] = begin;
else if(nums[end] == target)
res[0] = end;
else
return res;
begin = 0;
end = nums.size() - 1;
while(begin + 1 < end)
int mid = begin + (end - begin) / 2;
if(nums[mid] < target)
begin = end;
else if(nums[mid] > target)
end = mid;
else
//如果相等 继续向右找,能找到最后一个目标值的位置
begin = mid;
// 搜索右边的索引
if(nums[end] == target)
res[1] = end;
else if(nums[begin] == target)
res[1] = begin;
else
return res;
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果不在数组中返回按照顺序插入的位置索引。(假设数组中无重复元素)
思路:找到第一个大于等于target的位置
int searchInsert(vector<int>& nums, int target)
if(nums.empty()) return 0;
int begin = 0, end = nums.size() - 1;
while(begin + 1 < end)
int mid = begin + (end - begin) / 2;
if(nums[mid] > target)
end = mid;
else if(nums[mid] < target)
begin = mid;
else
begin = mid;
if(nums[begin] >= target)
return begin;
else if(nums[end] >= target)
return end;
else if(nums[end] < target)
return end + 1; //目标值比所有值都大
return 0;
编写一个高效算法,判断mxn中,是否存在一个目标值。
该矩阵的特点:
- 每行中整数从左到右按升序排序
- 每行的第一个整数大于前一个行的最后一个整数
思路:将2维的数组转化为1维数组,进行二分搜索。按行展开就是一个升序数组
bool searchMatrix(vector<vector<int>>& matrix, int target)
if(matrix.size() == 0 || matrix[0].size() == 0) return false;
int row = matrix.size(), col = matrix[0].size();
int begin = 0, end = row * col - 1;
while(begin + 1 < end)
int mid = begin + (end - begin) / 2;
int val = matrix[mid/col][mid%col]; // 转化为二维数组对应的值
if(val < target)
begin = mid;
else if(val > target)
end = mid;
else
return true;
if(matrix[begin/col][begin%col] == target || matrix[end/col][end%col] == target)
return true;
return false;
假设有n个版本,找出导致之后所有版本出错的第一个错误的版本,可以通过调用bool isBadVersion(version)接口来判断版本号version是否在单元测试中出错。实现一个函数来查找第一个出错的版本,尽可能减少API的调用次数。
int firstBadVersion(int n)
int begin = 0, end = n;
while(begin + 1 < end)
int mid = begin + (end - begin) /2;
if(isBadVersion(mid))
end = mid;
else
begin = mid;
if(isBadVersion(begin)) return begin;
return end;
find-minimum-in-rotated-sorted-array
假设按照升序排序的数组在预先未知的某个点上进行了旋转。(如:[0 1 2 3 4 5]变为[4 5 0 1 2 3]请找出其中最小的元素。(不包含重复元素)
思路:最后一个值作为target,然后往左移动,最后比较start、end的值
int findMin(vector<int>& nums)
if(nums.size() == 0) return -1;
int begin = 0, end = nums.size()-1;
while(begin + 1 < end)
int mid = begin + (end - begin)/2;
if(nums[mid] <= nums[end])
// 右半边
end = mid;
else
// 左半边
begin = mid;
if(nums[begin] > nums[end])
return nums[end];
return nums[begin];
find-minimum-in-rotated-sorted-array-ii
假设按照升序排序的数组在预先未知的某个点上进行了旋转。(如:[0 1 2 3 4 5]变为[4 5 0 1 2 3]请找出其中最小的元素。(包含重复元素)
思路:和上题思路类似,只是需要跳过重复的元素。
int findMin(vector<int>& nums)
if(nums.size() == 0) return -1;
int begin = 0, end = nums.size()-1;
while(begin + 1 < end)
// 去除重复元素
while(begin < end && nums[end] == nums[end-1]) end--;
while(begin < end && nums[begin] == nums[begin+1]) begin++;
int mid = begin + (end - begin) /2;
if(nums[mid] <= nums[end])
end = mid;
else
begin = mid;
if(nums[begin] >= nums[end]) return nums[end];
return nums[begin];
search-in-rotated-sorted-array
假设按照升序排序的数组,在某点旋转数组,搜索一个给定的目标值,如果数组中有这个值返回其下标,如果不存在返回-1。(不包含重复元素)
思路:两条上升直线,四种情况。面试的时候可以画图说明
int search(vector<int>& nums, int target)
if(nums.empty()) return -1;
int begin = 0, end = nums.size()-1;
while(begin + 1 < end)
int mid = begin + (end - begin) /2.0;
if(nums[mid] == target) return mid;
// 判断在哪个区间
if(nums[begin] < nums[mid])
if(nums[begin] <= target && target <= nums[mid])
end = mid;
else
begin = mid;
else if(nums[end] > nums[mid])
if(nums[end] >= target && target >= nums[mid])
begin = mid;
else
end = mid;
if(nums[begin] == target) return begin;
else if(nums[end] == target) return end;
return -1;
search-in-rotated-sorted-array-ii
假设按照升序排序的数组,在某点旋转数组,搜索一个给定的目标值,如果数组中有这个值返回true,如果不存在返回false。(包含重复元素)
bool search(vector<int>& nums, int target)
if(nums.empty()) return false;
//和上题相比需要处理重复元素
int begin = 0, end = nums.size()-1;
while(begin + 1 < end)
// 处理重复元素
while(begin < end && nums[begin] == nums[begin+1]) begin++;
while(begin < end && nums[end] == nums[end-1]) end--;
int mid = begin + (end - begin) / 2;
if(nums[mid] == target) return true;
//判断在哪个区间
if(nums[begin] < nums[mid])
if(nums[begin] <= target && target <= nums[mid])
end = mid;
else
begin = mid;
else if(nums[end] >= nums[mid])
if(nums[end] >= target && target >= nums[mid])
begin = mid;
else
end = mid;
if(nums[begin] == target || nums[end] == target) return begin;
return false;
二分搜索在面试中很常见,再次总结下要点:
- 初始化:start=0,end=len-1;
- 循环退出条件:start+1<end;
- 比较中点和目标值;
- 判断最后两个元素是否符合: A[start]、A[end] ? target.
以上是关于算法模板-----二分查找的主要内容,如果未能解决你的问题,请参考以下文章
学习数据结构笔记(15) --- [二分查找算法(非递归)]
算法二分法 ② ( 排序数组中查找目标值 | 二分法的经典写法 | 在排序数组中查找元素的最后一个位置 | 二分法的通用模板 )