题目描述:
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
示例1:
输入: nums = [1,3,5,6], target = 5 输出: 2
提示:
nums
为无重复元素的升序排列数组。
解题1:暴力解法:直接遍历数组,与 target 进行比较,需要注意的是,如果 target 比数组中的某个元素大,则继续循环,如果等于或者小于某个元素,则直接返回该元素的下标。同时还有一点,如果数组已经遍历完了,仍然没有找到 target 的准确位置,那么我们就认为 target 比数组中的每个元素都大,则直接返回数组大小即为 target 的下标。此方法时间复杂度为 O(n),空间复杂度为 O(1)。代码如下:
public static int searchInsert(int[] nums, int target) {
for (int i = 0; i < nums.length; i++) {
if (target <= nums[i]) {
return i;
}
}
// 如果 target 大于所有元素,或者数组为空,直接返回数组大小
return nums.length;
}
解题2:二分法:升序且无重复元素的题目要求正好适用于二分法,如果有重复数据的话二分法返回的数据还不一定是唯一的。时间复杂度是 O(logn),空间复杂度是 O(1)。需要注意的是,使用二分法的时候需要处理好边界值,即到底是 while(left < right) 还是 while(left <= right),到底是 right = middle 呢,还是 right = middle - 1?
当 target 位于 [left, right] 的左闭右闭区间时,代码如下:
public static int searchInsert2(int[] nums, int target) {
int size = nums.length;
int left = 0;
// 定义 target 在左闭右闭的区间里,[left, right]
int right = size - 1;
// 当 left == right 时,区间 [left, right] 依然有效
while (left <= right) {
// 防止溢出,等同于(left + right) / 2
int middle = left + ((right - left) / 2);
if (nums[middle] > target) {
// target 在左区间, 所以[left, middle - 1]
right = middle - 1;
} else if (nums[middle] < target) {
// target 在右区间,所以[middle + 1, right]
left = middle + 1;
} else {
// nums[middle] == target
return middle;
}
}
return right + 1;
}
当 target 位于 [left, right) 的左闭右开区间时,代码如下:
public static int searchInsert3(int[] nums, int target) {
int size = nums.length;
int left = 0;
// 定义 target 在左闭右闭的区间里,[left, right)
int right = size;
// 当 left == right 时,在[left, right)区间无效
while (left < right) {
// 防止溢出,等同于(left + right) / 2,也等同于 left + ((right - left) / 2)
int middle = left + ((right - left) >> 1);
// target 在左区间,在[left, middle)中
if (nums[middle] > target) {
right = middle;
} else if (nums[middle] < target) {
// target 在右区间,在 [middle+1, right)中
left = middle + 1;
} else {
// nums[middle] == target
return middle;
}
}
return right;
}