
第一题:34. 在排序数组中查找元素的第一个和最后一个位置
题目链接:34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)
题目分析
题目:给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target,返回 -1, -1。 核心:分别二分查找左边界、右边界。左边界是第一个等于 target 的位置;右边界是最后一个等于 target 的位置。 边界注意:找到边界后,要校验边界是否合法、对应数值是否等于 target,防止 target 不存在。
算法原理
两次二分:
- 找左边界:
nums[mid] >= target收缩右边界 - 找右边界:
nums[mid] <= target收缩左边界,最后结果 - 1 才是最后一个 target 位置 拿到左右边界后必须校验,数组里没有 target 时返回 -1,-1
代码编写
cpp
vector<int> searchRange(vector<int>& nums, int target) {
int n = nums.size();
int left = 0, right = n;
// 找左边界
while(left < right) {
int mid = left + (right - left) / 2;
if(nums[mid] >= target) right = mid;
else left = mid + 1;
}
int l = left;
// 找右边界
left = 0, right = n;
while(left < right) {
int mid = left + (right - left) / 2;
if(nums[mid] <= target) left = mid + 1;
else right = mid;
}
int r = left - 1;
if(l < n && nums[l] == target) return {l, r};
return {-1, -1};
}
总结
重复有序数组专用,两次边界二分;校验是必写步骤,很多人漏这一步导致报错。
第二题:33. 搜索旋转排序数组
题目链接:33. 搜索旋转排序数组 - 力扣(LeetCode)
题目分析
题目:整数数组 nums 按升序排列,数组中的值互不相同。在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了旋转,使数组变为 nums \[k, nums k+1, ..., nums n-1, nums 0, nums 1, ..., nums k-1]。给你旋转后的数组 nums 和一个整数 target,如果 nums 中存在这个目标值,则返回它的下标,否则返回 -1。 核心:旋转数组一定有一半区间是有序的。判断左半段有序还是右半段有序,再看 target 是否落在有序区间内,以此收缩二分边界。
算法原理
旋转数组二段性:左右两段必有一段有序
- 判断左半段是否有序
nums[left] <= nums[mid]- 有序且 target 落在这个区间:去左边搜
- 否则去右边搜
- 右半段有序
- 有序且 target 落在这个区间:去右边搜
- 否则去左边搜
代码编写
cpp
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) return mid;
// 左半有序
if(nums[left] <= nums[mid]) {
if(nums[left] <= target && target < nums[mid]) right = mid - 1;
else left = mid + 1;
} else {
// 右半有序
if(nums[mid] < target && target <= nums[right]) left = mid + 1;
else right = mid - 1;
}
}
return -1;
}
总结
旋转数组二分重点:先识别有序区间,再判断 target 是否在有序区间内,是面试高频题。
