摘要
leetcode刷题笔记
如有错误,欢迎指正。
前言
加油
34、在排序数组中查找元素的第一个和最后一个位置(二分法)(查找区间)
描述&样例
给定一个增序的整数数组和一个值,查找该值第一次和最后一次出现的位置。
输入是一个数组和一个值,输出为该值第一次出现的位置和最后一次出现的位置(从 0 开始);如果不存在该值,则两个返回值都设为-1。
Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]
思路&代码
利用增序性质,使用二分法找到数组中第一个等于target 的位置和第一个大于target的位置减一。
使用左闭右开的写法。
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
if (nums.empty()) return vector<int>{-1, -1};
int lower = lower_bound(nums, target);
int upper = upper_bound(nums, target) - 1; // 这里需要减1位 (大于target向前进了一位)
if (lower == nums.size() || nums[lower] != target) {
return vector<int>{-1, -1};
}
return vector<int>{lower, upper};
}
int lower_bound(vector<int> &nums, int target) {
int l = 0, r = nums.size(), mid; //左闭右开
while (l < r) { mid = (l + r) / 2;
if (nums[mid] >= target) { //第一个位置
r = mid;
}
else {
l = mid + 1;
}
}
return l;
}
int upper_bound(vector<int> &nums, int target) {
int l = 0, r = nums.size(), mid;
while (l < r) { mid = (l + r) / 2;
if (nums[mid] > target) { //第二个位置
r = mid;
} else {
l = mid + 1;
}
}
return l;
}
};
总结
二分法是一个非常常用的算法,用于在多条排序记录中快速找到待查找的记录,将时间复杂度优化到了O(logn),但是需要一组序列本身是有序的。
写好一个二分算法只需要注意这4个地方:
1、赋初值,判断是左闭右闭[0, n-1] 还是左闭右开[0, n)
二分查找时区间的左右端取开区间还是闭区间在绝大多数时候都可以。提供两个小诀窍,第一是尝试熟练使用 一种写法,比如左闭右开(满足语言习惯)或左闭右闭(便于处理边界条件), 尽量只保持这一种写法;第二是在刷题时思考如果最后区间只剩下一个数或者两个数,自己的写 法是否会陷入死循环,如果某种写法无法跳出死循环,则考虑尝试另一种写法。
2、退出条件,有小于和小于等于两种写法,<=对应的是上面“右闭”的情况,因为最后一个可能是要查找的值。
3、在一些比较特殊的情况下middle = (left + right) / 2这种写法可能造成溢出问题,更加保险的写法是middle = left + (right - left) / 2。
602

被折叠的 条评论
为什么被折叠?



