题目
35. 搜索插入位置
难度:简单
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
- 请必须使用时间复杂度为 O(log n) 的算法。
nums为 无重复元素 的 升序 排列数组
整体思路
-
建模,划分蓝红区域;
-
设定
l和r的初始值,即l = -1,r = nums.length; -
设定循环条件,即
l + 1 != r,抑或者题目要求的值(比如找不到就返回-1); -
确定返回的是
l还是r; -
套用模板。
具体实现及细节
-
题目要求 “ 返回目标值索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置 ” ,表示我们需要返回的是 第一次“
>= target”的索引值 。 因此,我们划分 “< target”为蓝区,划分 “>= target”为红区。 -
设定
l=-1,r=nums.length,这样l永远处于蓝区的位置,r永远处于红区的位置(尤其当目标值索引为0或者nums.length时)。 -
设定循环条件
l + 1 != r,防止死循环。 当l+1=r,意味着l和r都处于红蓝边界,循环结束; 当l+2=r,意味着l和r之间隔着一个数,于是再次循环一次; 当l+3=r,意味着l和r之间隔着两个数,再次循环,结果为l+1=r或者l+2=r。 这样,也就保证了永远不会陷入死循环。 -
由题目要求,即“
>=target”,我们需要返回红区的索引值,因此我们返回的是r。 -
为什么是“
l = m”以及“r = m” ,而不是“l = m + 1”和“r = m - 1”呢? 由初始值l=-1,r=nums.length,且l+1!=r(需要进入循环),可得:中间值m的最小值为(-1 + 1)/2 = 0,最大值为(nums.length-2 + nums.length)/2 = nums.length-1。 则m的取值范围为[0,nums.length-1],刚好对应整个数组。 当然,“l = m + 1”和“r = m - 1”在特定的逻辑里面也是可行的。 -
至于取中间值,为什么推荐用
l + (r - l)/2,因为(l + r)/2可能会内存溢出。即整型的字节大小有所限制,当l和r很大时,可能会内存溢出,而(r - l)就不会。
class Solution {
public int searchInsert(int[] nums, int target) {
int l = -1, r = nums.length;
while(l + 1 != r){
int m = l + (r - l)/2;
if(target == nums[m]){
return m;
}else if(target > nums[m]){
l = m;
}else if(target < nums[m]){
r = m;
}
}
return r;
}
}
>>:右移。该数为正,则高位补0,若为负数,则高位补1。
>>>:无符号右移。高位补0。
进阶题目
34. 在排序数组中查找元素的第一个和最后一个位置
难度:中等
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。
解题思路
题目要求时间复杂度O(logn),这不就是妥妥的二分查找嘛。
这道题的难点在于边界问题,解决了边界问题,事情就好办了。
题目要求返回一个两个元素的数组,第一个元素为第一次==target的下标,第二个元素为最后一次==target的下标,否则返回[-1,-1]。
我们可以分两步走,分别找出“左端值”和“右端值”。
“左端值”:确定红蓝边界为<target和>=target。为什么=在右边呢?因为如果有多个相等目标数,我们需要返回最左边,即我们需要把区间往左靠,而>target情况下区间会往左靠。下面的右端值也是同样的道理。
可以知道,左端值有且只有在>=target中,因此我们让==target时返回下标,否则返回-1(即找不到相等数)。需要注意的是我们会多次循环,直到找到最左下标(相等数);
“右端值”:确定红蓝边界为<=target和>target。可以知道,右端值有且只有在<=target中,因此我们让==target时返回下标,否则返回-1(即找不到相等数)。需要注意的是我们会多次循环,直到找到最右下标(相等数)。
代码
class Solution {
public static int[] searchRange(int[] nums, int target) {
return new int[]{search(nums,target,0),search(nums,target,1)};
}
public static int search(int[] nums,int target,int index){
int l = -1, r = nums.length;
//假设为-1,即找不到相等数
int res = -1;
while(l + 1 != r){
//防止内存溢出
int mid = l + ((r - l) >> 1);
if (index == 0){
//“左端值”
if(nums[mid] < target){
l = mid;
} else{
r = mid;
if(nums[mid] == target){
res = r;
}
}
} else {
//“右端值”
if(nums[mid] > target){
r = mid;
} else{
l = mid;
if(nums[mid] == target){
res = l;
}
}
}
}
return res;
}
}