80岁老爷爷学算法之二分查找
今日老爷爷闲来无事,打开了leetcode,并凑巧打开了0x3f的二分查找题单,于是开始了学算法的一天,爷爷不懂什么是二分查找,戴着老花镜盯着屏幕上的 “有序数组、左闭右闭、边界收缩”,眉头皱成一团,嘴里还小声嘀咕:“这年轻人写的东西,怎么比当年的算盘口诀还绕。”
老爷爷看到了有一个0x3f的b站讲解视频,爷爷不懂b站是干什么的,只知道孙子总在b站看番,于是他不愿意点开这个链接,只能自己摸索了,于是开始尝试leetcode34. 在排序数组中查找元素的第一个和最后一个位置,老爷爷想用for循环去从头遍历,可是题目要求时间复杂度O(logn),老爷爷心想看来只能用所谓什么所谓的二分查找了,老爷爷上来先模仿了一下二分左闭右闭固定模板:
while (左 <= 右):没找完就接着找
算中间位置
中间数 >= 目标:答案在左边,右边界往左缩
中间数 < 目标:答案在右边,左边界往右缩
循环结束:left就是第一个>=目标的位置
爷爷英语不好,但也尝试了一下C++语言,看来爷爷的学习能力还是很强的:
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] >= target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
当然,老爷爷虽然年龄大,但还是比较理智的,让mid = left + (right - left) / 2,这样能避免两个大数相加导致的数值溢出问题。 同理,爷爷又写了一下查找最后target的代码:
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
接下来,可以正式解答这道题了,理智的爷爷首先看到了示例3
示例 3:
- 输入:
nums = [], target = 0- 输出:
[-1,-1]
于是他先进行了空数组判定:
if(nums.empty()) return {-1,-1};
现在爷爷已经学得差不多了,于是整理了一个完整的C++代码:
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
if(nums.empty()) return {-1,-1};
int left = 0,right = nums.size()-1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] >= target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
int start = left;
if (start >= nums.size() || nums[start] != target) {
return {-1, -1};
}
left = 0;
right = nums.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
int end = right;
return {start, end};
}
};
这里细心的爷爷写了一个小细节:if (start >= nums.size() || nums[start] != target) return {-1, -1};,
// 两道安检门,缺一不可
if (start越界 || start位置的数不是target) {
返回[-1,-1],说明没找到目标值
}
他笑着说:“这就跟我去粮站领粮食一样,先看粮本够不够数(不越界),再看粮本上的名字对不对(是目标值),少一步都领不了,这代码也是一个理儿,少一步就错!”
最后,老爷爷在力扣上提交了代码,成功ac,时间复杂度也符合了,老爷爷勇闯算法之路第一天学习部分成功结束,接下来就要开始练习了。
可能有人会问,老爷爷都八十岁了,为什么还要学算法?
因为他心里,一直藏着一个独自搭建智能体的梦想。
情节虽然虚构,算法真实有效