原题链接:https://leetcode.com/problems/search-insert-position/
我在github上的leetcode仓库:https://github.com/cooljacket/leetcodes
题意
给定一个排好序的数组(升序)和一个数字target。
1. 如果target在数组里,返回它的位置;
2. 否则返回它插入到数组后保持数组有序的位置。
思路1-线性查找
线性从后往前查找,直到找到,或者遇到比它小的值。
评价:这个实现很简单,虽然能够AC,不过时间复杂度为O(n),利用好原数组有序的性质,可以有更好的办法!(见思路2)
class Solution {
public:
// 1)线性扫描
int searchInsert(vector<int>& nums, int target) {
int len = nums.size(), pos = len - 1;
while (pos >= 0 && nums[pos] >= target) {
if (nums[pos] == target)
return pos;
--pos;
}
return pos + 1;
}
};
思路2-二分查找
对于有序数组的查找,肯定要想到二分啊。不过这里找不到的话,要返回target插入后的位置,这个有点麻烦,如何确定这个位置呢?
假设数组是[1, 3, 5, 7, 9]
分类讨论一下,其实总的只有三种情况是找不到的:
1)从左边溢出区间,比如target=0,最后的情况是left = right = 0,然后right - 1变成-1,跳出while循环,返回值应该是right + 1 = 0;
2)从右边溢出区间,比如target=10,最后的情况是left = right = 4,然后left + 1变成5,跳出while循环,返回值应该是right + 1 = 5;
3)处于区间的两个值之间,比如target=8位于7和9之间,最后的情况是left = right = 3,然后left + 1变成4,跳出while循环,返回值应该是right + 1 = 4。
综上所述,如果找不到,直接返回right + 1就是对的!!!
评价:虽然二分法很高效,复杂度为O(log n),不过分析起来没有线性查找那样直白,如果不是老司机,对于“找不到”的情况可能有点不知所措。(我也不是老司机)
class Solution {
public:
// 2)二分法!注意分析最后为什么是right + 1,找不到分三种情况~
// a) 从左边溢出区间;b)从右边溢出区间;c)陷于区间上两个相邻的值之间。
int searchInsert(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1, mid;
while (left <= right) {
mid = (left + right) >> 1;
if (nums[mid] == target)
return mid;
if (nums[mid] < target)
left = mid + 1;
else
right = mid - 1;
}
return right + 1;
}
};