基础知识要求:
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处(数组末尾之后)