1. 题目
搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n)
的算法。
示例 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
2.解题思路
时间复杂度解释:
时间复杂度是指算法在处理规模为 n 的输入数据时所需要的时间成长速度的一个量度。它是一个函数,描述了算法运行时间随输入规模增长的变化趋势。通常,我们用大 O 符号表示时间复杂度,简写为 O(f(n)),其中 f(n) 是运行时间与输入规模 n 的函数关系。
时间复杂度可以帮助我们评估不同算法在处理同样规模的数据时的性能差异。例如,如果一个算法的时间复杂度为 O(n),而另一个算法的时间复杂度为 O(n^2),那么当输入规模很大时,后者的运行时间将比前者长得多,这是因为当 n 增大时,n^2 的增长速度比 n 更快。
常见的时间复杂度包括:
- O(1):常数时间复杂度,表示算法的运行时间不随输入规模变化而变化。
- O(log n):对数时间复杂度,表示算法的运行时间与输入规模的对数成正比。
- O(n):线性时间复杂度,表示算法的运行时间与输入规模成正比。
- O(n log n):线性对数时间复杂度,表示算法的运行时间与输入规模的对数乘积成正比。
- O(n^2):平方时间复杂度,表示算法的运行时间与输入规模的平方成正比。
- O(2^n):指数时间复杂度,表示算法的运行时间随输入规模的指数增长。
时间复杂度为 O(log n) 表示算法的运行时间与输入规模 n 的对数成正比。这意味着,随着输入规模 n 的增加,算法的运行时间增长,但增长速度是逐渐减慢的。这种类型的算法通常是高效的,因为它们能够在相对较短的时间内处理大量数据。
对数时间复杂度常见于分治算法、二分查找、某些排序算法(如归并排序和快速排序的平均情况)、以及许多基于树和图的算法。例如,在二分查找中,每次迭代都将搜索范围减半,因此查找一个元素需要大约 log₂n 次比较。
数学上,对数函数 log(n) 的增长,随着 n 的增大而变得越来越慢。例如,log₂1024 = 10,而 log₂1048576 = 20,尽管 1048576 是 1024 的 1024 倍,但对数只增加了 10。
解题思路如下:
理解问题:
- 给定一个排序数组,意味着数组中的元素是有序的,没有重复元素。
- 需要找到目标值在数组中的索引,如果目标值不存在,则返回它应该插入的位置,以保持数组的有序性。
由于数组是有序的,且要求时间复杂度为 O(log n),因此选择二分查找算法。
二分查找算法:
- 初始化两个指针,
left
指向数组的第一个元素,right
指向数组的最后一个元素。- 进入循环,计算中间元素的索引
mid
,比较nums[mid]
和target
:
- 如果
nums[mid]
等于target
,直接返回mid
。- 如果
nums[mid]
小于target
,说明target
在mid
的右侧,更新left
为mid + 1
。- 如果
nums[mid]
大于target
,说明target
在mid
的左侧,更新right
为mid - 1
。- 重复上述步骤,直到
left
大于right
。- 当循环结束时,
left
指向的位置就是target
应该插入的位置。返回结果:
- 如果在数组中找到了
target
,返回其索引。- 如果
target
不存在于数组中,返回left
,即target
应该插入的位置。
int searchInsert(int* nums, int numsSize, int target) {
// 初始化左指针为0,即数组的起始位置
int left = 0;
// 初始化右指针为数组长度减1,即数组的最后一个元素的位置
int right = numsSize - 1;
// 声明中间变量mid,用于存储每次循环的中间索引位置
int mid;
// 当左指针不大于右指针时,继续进行二分查找
while (left <= right) {
// 计算中间索引位置,防止由于直接相加导致整数溢出
mid = left + (right - left) / 2;
// 如果中间元素等于目标值,返回中间索引
if (nums[mid] == target) {
return mid;
}
// 如果中间元素小于目标值,说明目标值在中间元素的右侧
// 更新左指针到中间索引的下一个位置
if (nums[mid] < target) {
left = mid + 1;
} else {
// 如果中间元素大于目标值,说明目标值在中间元素的左侧
// 更新右指针到中间索引的前一个位置
right = mid - 1;
}
}
// 如果循环结束还没有找到目标值,说明目标值不在数组中
// 此时left指向的是目标值应该插入的位置,返回left
return left;
}