题目描述
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
题解示例
- 示例 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
初次解题
class Solution {
public int searchInsert(int[] nums, int target) {
// 初始化索引 i 为 0
int i = 0;
// 如果数组长度为 1
if (nums.length == 1) {
// 如果数组中唯一的元素小于目标值,返回 1,表示目标值应插入在这个元素之后
if (nums[0] < target) {
return 1;
} else {
// 如果数组中唯一的元素大于等于目标值,返回 0,表示目标值应插入在这个元素之前(即数组开头)
return 0;
}
}
// 当 i 小于数组长度减 1 时循环
while (i < nums.length - 1) {
// 如果当前元素小于目标值
if (nums[i] < target) {
// 如果下一个元素大于等于目标值,返回 i + 1,表示目标值应插入在当前元素之后
if (nums[i + 1] >= target) {
return i + 1;
} else {
// 如果下一个元素也小于目标值,继续向后查找,i 自增
i++;
}
} else {
// 如果当前元素大于等于目标值,返回 0,表示目标值应插入在当前元素之前(即数组开头)
return 0;
}
}
// 如果循环结束还没找到合适位置,说明目标值大于数组中所有元素,返回数组长度,表示目标值应插入在数组末尾
return nums.length;
}
}
复杂度分析
时间复杂度:在最坏的情况下,需要遍历整个数组,直到
i
达到nums.length - 1
。如果数组长度为n
,那么时间复杂度为O(n) 。
改进的做法
class Solution {
public int searchInsert(int[] nums, int target) {
// 定义左边界指针和右边界指针
int left = 0;
int right = nums.length - 1;
// 当左边界不超过右边界时进行循环查找
while (left <= right) {
// 计算中间位置
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
// 找到目标值,返回其索引
return mid;
} else if (nums[mid] < target) {
// 目标值在中间值右侧,更新左边界
left = mid + 1;
} else {
// 目标值在中间值左侧,更新右边界
right = mid - 1;
}
}
// 未找到目标值,返回左边界,即目标值应插入的位置
return left;
}
}
注意在数组中间下标的计算推荐使用
int mid = left + (right - left) / 2;
而不是,后者在一些情况下可能导致查找不准确或陷入死循环,会产生程序超时的情况发生
int mid = right / 2;
改进的好处
采用的是二分查找法,每次循环都将搜索范围缩小一半,所以时间复杂度为O(log(n)) ,其中
n
是数组nums
的长度。
二分查找法回顾
二分查找法:用于在有序数组中查找特定元素。(如果无序要先排序)
二分查找步骤
- 首先,确定一个查找区间,初始时,区间的左边界为数组的第一个元素索引,右边界为数组的最后一个元素索引。
- 然后,计算区间的中间位置,可以通过公式mid = left + (right - left) / 2来计算,这样可以避免整数溢出。
- 接着,将中间位置的元素与目标元素进行比较: 如果中间元素等于目标元素,查找成功,返回中间元素的索引。 如果中间元素大于目标元素,说明目标元素在中间元素的左侧,此时将右边界更新为mid - 1,缩小查找区间继续查找。 如果中间元素小于目标元素,说明目标元素在中间元素的右侧,此时将左边界更新为mid + 1,缩小查找区间继续查找。
- 重复上述步骤,直到找到目标元素或者左边界超过右边界(表示目标元素不在数组中)。