二分查找算法详解:基础查找与边界定位
二分查找算法原理及实现。涵盖标准二分查找与有序数组中查找元素首尾位置。通过代码示例展示左右指针移动逻辑、中间值计算防溢出技巧,以及寻找左右边界时的取整差异。重点分析区间收缩策略,避免死循环,提供 C++ 模板代码。

二分查找算法原理及实现。涵盖标准二分查找与有序数组中查找元素首尾位置。通过代码示例展示左右指针移动逻辑、中间值计算防溢出技巧,以及寻找左右边界时的取整差异。重点分析区间收缩策略,避免死循环,提供 C++ 模板代码。

二分查找(Binary Search),也称为折半查找,是一种高效的有序数组查找算法。其核心思想是通过不断将搜索区间减半,快速缩小目标值的可能范围,最终找到目标值或确定其不存在。该算法的时间复杂度为 O(log n),远优于顺序查找的 O(n),在处理大规模有序数据时优势显著。
题目链接:
题目描述:
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
解法:
arr[mid] == target:说明正好找到,返回 mid 的值;arr[mid] > target:说明 [mid, right] 这段区间都是大于 target 的。因此舍去右边区间,往左边 [left, mid-1] 的区间继续查找,即让 right = mid - 1,然后重复步骤 2;arr[mid] < target:说明 [left, mid] 这段区间的值都是小于 target 的。因此舍去左边区间,在右边 [mid+1, right] 区间继续查找,即让 left = mid + 1,然后重复步骤 2。class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2; // 防止数据溢出
if (nums[mid] < target) left = mid + 1;
else if (nums[mid] > target) right = mid - 1;
else return mid;
}
return -1;
}
};
int left = 0, right = nums.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2; // 防止数据溢出
if (条件判断) left = mid + 1;
else if (条件判断) right = mid - 1;
else return 结果;
}
请大家一定不要觉得背下模板就能解决所有二分问题。二分问题最重要的就是要分析题意,然后确定要搜索的区间,根据分析问题来写出二分查找算法的代码。要分析题意,确定搜索区间,不要死记模板,不要看左闭右开什么乱七八糟的题解。
模板记忆技巧:
right - 1 的情况下,才会向上取整(也就是 +1 取中间数)。题目链接:
题目描述:
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
解法:
用的还是二分思想,就是根据数据的性质,在某种判断条件下将区间一分为二,然后舍去其中一个区间,然后在另一个区间内查找。
方便叙述,用 x 表示该元素,resLeft 表示左边界,resRight 表示右边界。
寻找左边界:我们需要注意到以左边界划分的两个区间的特点:
[left, resLeft - 1] 都是小于 x 的;[resLeft, right] 都是大于等于 x 的;因此,关于 mid 的落点,我们可以分为下面两种情况:
[left, resLeft - 1] 区间的时候,也就是 arr[mid] < target。说明 [left, mid] 都是可以舍去的,此时更新 left 到 mid + 1 的位置,继续在 [mid + 1, right] 上寻找左边界;[resLeft, right] 的区间的时候,也就是 arr[mid] >= target。说明 [mid + 1, right] (因为 mid 可能是最终结果,不能舍去)是可以舍去的,此时更新 right 到 mid 的位置,继续在 [left, mid] 上寻找左边界;由此,就可以通过二分,来快速寻找左边界;注意:这里找中间元素需要向下取整。因为后续移动左右指针的时候:
left = mid + 1,是会向后移动的,因此区间是会缩小的;right = mid,可能会原地踏步(比如:如果向上取整的话,如果剩下 1,2 两个元素,left == 1,right == 2,mid == 2。更新区间之后,left,right,mid 的值没有改变,就会陷入死循环)。因此一定要注意,当 right = mid 的时候,要向下取整。
寻找右边界:用 resRight 表示右边界,我们注意到右边界的特点:
[left, resRight] 都是小于等于 x 的;[resRight + 1, right] 都是大于 x 的;因此,关于 mid 的落点,我们可以分为下面两种情况:
[left, resRight] 区间的时候,说明 [left, mid - 1] (mid 不可以舍去,因为有可能是最终结果)都是可以舍去的,此时更新 left 到 mid 的位置;[resRight + 1, right] 的区间的时候,说明 [mid, right] 内的元素是可以舍去的,此时更新 right 到 mid - 1 的位置;由此,就可以通过二分,来快速寻找右边界;注意:这里找中间元素需要向上取整。因为后续移动左右指针的时候:
left = mid,可能会原地踏步(比如:如果向下取整的话,如果剩下 1,2 两个元素,left == 1,right == 2,mid == 1。更新区间之后,left,right,mid 的值没有改变,就会陷入死循环)。right = mid - 1,是会向前移动的,因此区间是会缩小的;因此一定要注意,当 left = mid 的时候,要向上取整。
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int begin = 0, end = 0, mid = 0;
if (nums.empty()) {
return {-1, -1};
}
// 1. 查找区间左端点
int left = 0, right = nums.size() - 1;
while (left < right) {
mid = left + (right - left) / 2;
if (nums[mid] < target) left = mid + 1;
else right = mid;
}
if (nums[left] == target) begin = left;
else return {-1, -1};
// 2. 查找区间右端点
left = 0, right = nums.size() - 1;
while (left < right) {
mid = left + (right - left + 1) / 2;
if (nums[mid] <= target) left = mid;
else right = mid - 1;
}
if (nums[right] == target) end = right;
else return {-1, -1};
return {begin, end};
}
};
笔记展示略。
二分查找算法总结:请大家一定不要觉得背下模板就能解决所有二分问题。二分问题最重要的就是要分析题意,然后确定要搜索的区间,根据分析问题来写出二分查找算法的代码。要分析题意,确定搜索区间,不要死记模板,不要看左闭右开什么乱七八糟的题解。
模板记忆技巧:
right - 1 的情况下,才会向上取整(也就是 +1 取中间数)。
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online