基础知识要求:
Java:方法、while循环、if else语句、数组
Python: 方法、while循环、if else语句、列表
题目:
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 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
提示:
1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums
为 无重复元素 的 升序 排列数组-104 <= target <= 104
思路解析:
这个问题是一个典型的二分查找问题,但有一个额外的要求:如果目标值不存在于数组中,需要返回它应该被插入的位置。这意味着我们不仅要找到目标值是否存在于数组中,还要找到它应该被插入的位置,即使它不存在。
以下是解决这个问题的思路:
-
初始化搜索边界:将搜索的左边界
left
设置为数组的起始位置(索引0),将右边界right
设置为数组的最后一个位置(索引len(nums) - 1
)。 -
进行二分查找:
- 计算中间位置的索引
mid
,使用mid = left + (right - left) / 2
(整除以避免整数溢出)。 - 比较
nums[mid]
和target
:- 如果
nums[mid] == target
,则找到了目标值,直接返回mid
。 - 如果
nums[mid] < target
,则目标值(如果存在)一定在mid
的右侧,更新left = mid + 1
。 - 如果
nums[mid] > target
,则目标值(如果存在)一定在mid
的左侧或就是mid
(但由于我们已经检查了mid
,所以知道它不等于target
),更新right = mid - 1
。
- 如果
- 计算中间位置的索引
-
处理目标值不存在的情况:
- 如果循环结束后仍未找到目标值(即
left > right
),则目标值应该被插入在left
的位置。这是因为在最后一次迭代中,nums[mid]
必然大于target
,且mid
被更新为了right
(即mid - 1
),而left
保持不变。因此,left
的位置是第一个大于target
的元素的位置,也是target
应该被插入的位置。
- 如果循环结束后仍未找到目标值(即
-
返回结果:返回
left
作为结果,它要么是目标值的索引,要么是目标值应该被插入的位置。
这个算法的关键在于理解二分查找过程中如何更新 left
和 right
,以及在何时知道目标值不存在于数组中,并确定其应该被插入的位置。
Java代码示例:
public class Solution {
/**
* 在有序数组中查找目标值,如果不存在则返回应该插入的位置
*
* @param nums 有序数组
* @param target 目标值
* @return 目标值的索引(如果存在),否则为应该插入的位置
*/
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;
// 如果中间元素小于目标值,说明目标值(如果存在)一定在mid的右侧,更新左边界
} else if (nums[mid] < target) {
left = mid + 1;
// 如果中间元素大于目标值,说明目标值(如果存在)一定在mid的左侧或就是mid(但已经检查过mid不等于target),更新右边界
} else {
right = mid - 1;
}
}
// 如果循环结束,说明目标值不存在于数组中,left将会是target应该被插入的位置
// 这是因为当left > right时,left指向了下一个应该被插入的位置
return left;
}
/**
* 主方法,用于测试searchInsert方法
*
* @param args 命令行参数(本例中未使用)
*/
public static void main(String[] args) {
Solution solution = new Solution();
int[] nums = {1, 3, 5, 6}; // 初始化一个有序数组
// 测试目标值为5的情况
int target = 5;
System.out.println(solution.searchInsert(nums, target)); // 输出: 2
// 测试目标值为2的情况
target = 2;
System.out.println(solution.searchInsert(nums, target)); // 输出: 1
// 测试目标值为7的情况
target = 7;
System.out.println(solution.searchInsert(nums, target)); // 输出: 4
}
}
Python代码示例:
def searchInsert(nums, target):
# 初始化搜索的左右边界,left为数组的开始位置,right为数组的结束位置
left, right = 0, len(nums) - 1
# 当左边界小于等于右边界时,进行循环搜索
while left <= right:
# 计算中间索引位置
mid = (left + right) // 2
# 如果中间元素等于目标值,直接返回其索引
if nums[mid] == target:
return mid
# 如果中间元素小于目标值,说明目标值(如果存在)一定在mid的右侧,更新左边界
elif nums[mid] < target:
left = mid + 1
# 如果中间元素大于目标值,说明目标值(如果存在)一定在mid的左侧或就是mid(但已经检查过mid不等于target),更新右边界
else:
right = mid - 1
# 如果循环结束,说明目标值不存在于数组中
# 由于我们是通过 left > right 退出的循环,且最后一次比较时 nums[mid] > target
# 所以 target 应该被插入在 left 的位置(此时left是第一个大于target的元素的位置)
# 返回left作为插入位置
return left
# 测试
nums = [1,3,5,6]
target = 5
print(searchInsert(nums, target)) # 输出: 2 # 目标值5在索引2处
target = 2
print(searchInsert(nums, target)) # 输出: 1 # 目标值2应该被插入在索引1处
target = 7
print(searchInsert(nums, target)) # 输出: 4 # 目标值7应该被插入在索引4处(数组末尾之后)