给出一个已经排好序由不同整数组成的数组和一个目标值,如果在数组中找到了目标值返回对应的下标,如果没有找到目标值返回目标值按照数组中数字的顺序应该出现的位置。
你必须写一个时间复杂度为O(logn)的算法。
我拿到题目的第一想法就是在整个数组中按照顺序从第一个元素向后面依次查找,如果原数组nums中存在目标值target,那么一定存在一个i使得nums[i]=targetnums[i]=targetnums[i]=target如果在原数组nums中不存在target那么一定存在这样的相邻的两个数nums[i]与nums[i+1]使得nums[i]<target<nums[i+1]nums[i]<target<nums[i+1]nums[i]<target<nums[i+1]
此时target应该位于的位置就是i+1的位置。将两个条件进行合并可以得到这样的条件nums[i]<target≤nums[i+1]nums[i]<target \leq nums[i+1]nums[i]<target≤nums[i+1]
但是注意第一个元素之前是不存在其他元素的,所以第一个元素要进行特殊判定,判定条件为:
target≤nums[0]target \leq nums[0]target≤nums[0]
如果成立,那么target元素的位置就为0.
我们再分析一下nums[i]<target≤nums[i+1]nums[i]<target \leq nums[i+1]nums[i]<target≤nums[i+1]
如果不成立只可能是target<=nums[i]target<=nums[i]target<=nums[i]不成立,这个很好证明(提示一下,上一个元素位置判定失败才会进入下一个位置的判定),所以可以将判定条件进一步简化为target<=nums[i]target<=nums[i]target<=nums[i]
经过上面的分析,我们可以写出如下的代码:
int searchInsert(int * nums, int numsSize, int target){
for(int i=0; i<numsSize; i++){
if(target<=nums[i]){
return i;
}
}
return numsSize;
}
但是这一种算法的时间复杂度是O(n)的,题目要求的时间复杂度是O(logn)的,所以说有没有更好办法使得我们所写的代码的时间复复杂度更小。
我们再来看一下给我们的是一个什么样的数组呢?已经排好序的数组对不对,我们来看一下已经排好序的数组有什么特点,假如说nums[i]<target成立那么对于所有的nums[i-1]、nums[i-2]…nums[0]都小于target,所以假如我们将数组从中间一分为2,取中间元素与target比较,则有以下三种情况:
target<nums[middle]target<nums[middle]target<nums[middle]
target=nums[middle]target=nums[middle]target=nums[middle]
target>nums[middle]target>nums[middle]target>nums[middle]
其中当target=nums[middle]成立时,说明此位置就是target的位置,其他的两种情况,我们至少可以排除一半的元素。然后再在剩下的一般元素中采用上述方法进行查找,如此重复,只要nums中含有target就能找到,但这一种情况是考虑nums数组中存在目标值target的,但是如果nums中不存在目标值target的话,那么就没有target=nums[middle],那我们该如何进行考虑呢?既然nums数组中不含有target值,那么就说明nums[i]<target<nums[i+1],我们在上述的查找过程中,就会不断的缩小查找的范围对吧,我们以5个元素的数组举一个例子,
nums = {0, 1, 2, 3, 4}
target = 6
1.此时判定范围为下界down = 0,上界up=4,middle = (down+up)/2 = 2
2. nums[2]<target,变换下界down = middle+1,此时查找的中间元素为middle = (down+up)/2 = 3
3.nums[3]<target,变换下界down = middle+1,middle=(up+down)/2=up=down此时down==up,查找范围内就剩下一个元素了
4.nums[4]<target说明target在nums不存在,且存在于最后查找的元素的右边,即输出最后的middle+1
经过上面的例子,我们清晰的知道了,如果在nums中target如果不存在最后一定会缩小到一个元素,然后比较其与target的大小,可以判定target应该位于最优一个查找元素的左边或者右边。
现在经过上面的分析,我们对于nums中有没有target,我们来判断target的位置都很清楚了。
int searchInsert(int *nums, int numsSize, int target){
int left = 0,right = numsSize-1;
int answer,middle;
while(right>=left){
middle = (left+right)/2;
if(nums[middle]==target){
return middle;
}else if(nums[middle]>target){
right = middle-1;
answer = middle;
}else{
left = middle+1;
answer = left;
}
}
return answer;
}
提交结果截图: