一、题目描述
原题连接: LeetCode 35. 搜索插入位置
题目描述:
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,则返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
示例 1:
输入: nums = [1,3,5,6], target = 5
输出: 2
示例 2:
输入: nums = [1,3,5,6], target = 2
输出: 1
示例 3:
输入: nums = [1,3,5,6], target = 7
输出: 4
二、解题
方法1——直接遍历
1.1、思路分析
直接遍历数组,如果找到target就返回其下标,当出现nums[i] < target && nums[i + 1] > target时,i + 1就是我们要顺序插入的下标。
1.2、代码实现
有了以上思路,那我们写起代码来也就水到渠成了:
int searchInsert1(int* nums, int numsSize, int target) {
assert(nums);
// 特殊情况特殊处理
if (target < nums[0]) {
return 0;
} else if (target > nums[numsSize - 1]) {
return numsSize;
}
int i = 0;
for (i = 0; i < numsSize; i++) {
if (nums[i] == target) {
return i;
}
else if (nums[i] < target && nums[i + 1] > target) {
return i + 1;
}
}
return numsSize;
}
时间复杂度:O(n),n为数组元素个数,我们只需要遍历一遍数组即可。
空间复杂度:O(1),我们只需要用到常数级的额外空间。
方法2——二分法
2.1、思路分析
注意题目中说的是一个排序数组,那么上面的方法就显得太复杂了,在一个排序数组中寻找一个目标值,我们很容易的就能想到用二分法。
但有些人可能就会疑惑,用二分法查找如果找到了那还好说,但若是找不到呢?该怎么找到要顺序插入的下标呢?
这个其实很简单,我们直接返回left就行,思路如下:
当我们停下循环的时候,一定是left > right 的时候,这也一定是left向后移动的结果,而且只有当nums[mid] > target的时候才执行的left = mid + 1 (这里的mid是上一轮的mid,也即left == right时mid)
所以可以肯定的是,上一轮mid指向的元素小于target,
那么怎么确定当left > right时,left指向的元素就一定大于target呢?
请看下图:
我们需要思考,为什么left和right会重合呢?是不是当mid指向的元素大于target的时候,执行的right = mid - 1才能让left和right重合呢?所以我们可以确定当left和right将要重合是,上一轮的left、mid和right一定是像上图一样,排成一排的。
所以如果下一轮(left和right重合时),left还要左移的话,就只能移动到上一轮mid指向的位置:
所以我们也就能确定,当left > ritht时,有nums[right] < target < nums[left]了。
故我们最后直接返回left即可。
2.2、代码实现
有了以上分析,那我们写起代码来也就水到渠成了:
int searchInsert2(int* nums, int numsSize, int target) {
assert(nums);
// 特殊情况特殊处理
if (target < nums[0]) {
return 0;
}
else if (target > nums[numsSize - 1]) {
return numsSize;
}
int left = 0;
int right = numsSize - 1;
int mid = 0;
while (left <= right) {
mid = left + (right - left) / 2;
if (target == nums[mid]) {
return mid;
}
else if (target > nums[mid]) {
left = mid + 1;
}
else if (target < nums[mid]) {
right = mid - 1;
}
}
return left;
}
时间复杂度:O(logn),n为数组元素个数。
空间复杂度:O(1),我们只需要用到常数级的额外空间。