题目描述:
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 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
解题思路:
此题需要解决两个问题:
(1)在数组中找到和目标元素相等的那个元素
(2)没有相等的元素,找到插入位置
而这两个问题实际上对应着四种情况:
(1)目标值target与数组中某个位置的值相等
(2) 目标值在数组范围内
(3) 目标值在数组范围外,小于所有元素
(4)目标值在数组范围外,大于所有元素
同时考虑到数组是顺序排列的,所以可以使用暴力解法和二分查找。
解题方法及解释:
方法1 暴力解法
就是target和数组中的每个元素挨个进行比较,然后找到目标值的位置
class Solution {
public int searchInsert(int[] nums, int target) {
for(int i=0; i<nums.length; i++){
if(nums[i]>= target){
return i;
}
}
return nums.length;
}
}
为什么返回值是i :
如果有相等的元素则直接返回其所在的位置i, 如果没有但可以插入区间中的话,就是看target小于哪个值,小于的那个值的位置就是要插入的位置,因为target前面的数都是小于target的或者target直接就小于第一个值。
为什么返回值是nums.length:
如果元素比区间中所有的值都大的话就要查到数组的最后。
方法2 二分查找
二分查找第一种写法 [left,right] 左闭右闭,此方法有两个返回结果,下面进行解释
(1)返回值是mid和left
class Solution {
public int searchInsert(int[] nums, int target) {
int n = nums.length;
int left = 0;
int right = n-1;
while(left <= right){
int mid = ((right -left)>> 1) + left;
if(target == nums[mid]){
return mid;
}else if(target < nums[mid]){
right = mid -1;
}else{
left = mid +1;
}
}
return left; /// 返回值是left
}
}
(2)返回值是mid和right+1
class Solution {
public int searchInsert(int[] nums, int target) {
int n = nums.length;
int left = 0;
int right = n-1;
while(left <= right){
int mid = ((right -left)>> 1) + left;
if(target == nums[mid]){
return mid;
}else if(target < nums[mid]){
right = mid -1;
}else{
left = mid +1;
}
}
return right + 1; ///返回值是right +1
}
}
为什么left <= right:
因为二分查找一直需要去缩小范围直到找到所有的数字,缩小范围意味着边界变小,也就是左加右减,只有当左边界小于等于右边界时比较才有意义,也就是循环才能继续下去。而在这个过程中总会有右边界不断减直到小于左边界的时候,这个时候区间就无意义了,也就是再不能再缩小区间了,也就是循环结束了。
为什么返回值是mid:
因为target始终要和mid进行比较,如果是情况1(等于)的话,就是我们想要的最理想的结果,找到这个值, target == nums[mid] 那就直接返回mid。
为什么返回值是right+1或者left:
还是target始终要和mid进行比较,如果不等于其中的元素,就需要缩小边界也就是左加右减,如果找到位置了必然会跳出循环,也就是右小于左,而此时left的位置就是要插入的位置(可以演算一下),而此时right +1 = left(包含了首位置[0],和尾位置[n])。
二分查找第二种写法 [left,right)左闭右开
class Solution {
public int searchInsert(int[] nums, int target) {
int n = nums.length;
int left = 0;
int right = n;
while(left < right){
int mid = ((right - left) >>1) + left;
if(target < nums[mid]){
right = mid;
}else if( target > nums[mid]){
left = mid +1;
}else{
return mid;
}
}
return right;
}
}
为什么是left < right:
因为左比右开区间不能出现边界值相等的情况
为什么right = mid:
因为相比于上一解法的闭区间,开区间必须大一位才能包含数组中所有的整数,而mid 所在的位置刚好就是right的位置。