35. 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
示例 1:
输入: [1,3,5,6], 5
输出: 2
解法一
可以顺序搜索查找,比当前大就往下遍历,比当前小就返回当前的index,最后返回数组长度(即插在最后一个index的后面)
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
for (int i = 0; i < nums.size(); ++i) {
if (target > nums[i]) {
;
} else {
return i;
}
}
return nums.size();
}
};
解法二
可以二分查找,但二分查找虽然时间复杂度低一些,但是中间需要考虑的边界条件多一些,有两个地方需要考虑
- 搜索到边界时需要退出并返回边界(插在最后一个位置后面)。
- 当搜索到的start和end相邻时,mid为start,如果此时nums[mid]<target,则正常逻辑start更新为mid,则导致无限循环。
所以代码中需着重考虑这两点
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int start = 0;
int end = nums.size();
while (start <= end) {
int mid = (start + end) / 2;
if (nums[mid] < target) {
if (start == mid) {
return end;
}
start = mid;
} else if (nums[mid] > target) {
if (start == mid) {
return start;
}
end = mid;
} else {
return mid;
}
}
return start;
}
};
但这样的思考显然过于复杂,写二分查找也不通用,所以看了一个题解后掌握了一个重要的思想:从一个元素什么时候不是解开始考虑下一轮搜索区间是什么,也就是要注意搜索区间。由于mid的范围为[start, end), 但是我们搜索区间的范围是[start, end], 所以一开始 start=0, end=nums.size(), 这样不用担心mid越界的问题,也包含了所有解的空间。之后每次更新区间都要注意这个范围的意义和前面保持一致。
由于start和end都是根据mid来更新,所以mid不可能取到end,只有可能取到start。且由于mid的范围限制,循环时判断start == end时就应该退出并返回start。
更新时如果nums[mid]<target,那么搜索区间肯定不包含mid之前包括mid的部分,最小也是mid+1,所以本着搜索区间应该是[start, end]的原则,start更新为mid+1。
反之如果 nums[mid]>target,那么搜索区间有可能包含mid,所以end更新为mid。
如果相等,则返回mid即可。
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int start = 0;
int end = nums.size();
while (start != end) {
int mid = (start + end) / 2;
if (nums[mid] < target) {
start = mid + 1;
} else if (nums[mid] > target) {
end = mid;
} else {
return mid;
}
}
return start;
}
};