35. Search Insert Position

题目描述

Given a sorted array of distinct integers and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.
You must write an algorithm with O ( l o g n ) O(logn) O(logn) runtime complexity.


思路

首先,我们注意到该数组已排序,且题目对时间复杂度有要求,为 O ( l o g n ) O(logn) O(logn)。显然,我们需要用二分查找。但是,需要注意的是,若没有找到目标值,我们要返回目标值待插入的下标。因此,我们需要对二分查找进行一些修改。


二分查找

我们首先来看二分查找。
一个升序数组nums,我们要查找target的值为65。
图一
我们声明三个变量leftrightmid

  • lefttarget所在区间的最左端元素的数组下标
  • righttarget所在区间的最右端元素的数组下标
  • midtarget所在区间的中间元素的数组下标
    m i d = l e f t + r i g h t 2 mid=\frac{left+right}{2} mid=2left+right

显然,nums[mid]将数组nums分成两个区间。接着,我们将nums[mid]target比较大小,

  • nums[mid] > target,则target位于区间内,调整rightmid - 1
  • nums[mid] < target,则target位于区间内,调整leftmid + 1
  • nums[mid] = target,则恰好找到target对应的数组下标,返回mid

然后,我们重复以上内容,不断缩小区间,直至找到target(若存在)。


因为数组nums按升序排列,所以,如果target在数组中的话,一定在数组首、末元素组成的区间内。因此,我们给left赋值0(第一个数组元素的下标),给right赋值nums_size - 1sums_size为数组元素个数。right的值意味着最后一个数组元素的下标,此处rihgt的值为9)。此时,mid的值为4

  • left = 0
  • right = 9
  • mid = 4

图二
∵ n u m s [ 4 ] = 58 , t a r g e t = 65 ∴ n u m s [ 4 ] < t a r g e t ∴ l e f t = m i d + 1 = 5 \begin{align*} &\because nums[4]=58,target=65 \\ &\therefore nums[4]<target \\ &\therefore left=mid+1=5 \end{align*} nums[4]=58,target=65nums[4]<targetleft=mid+1=5


  • left = 5
  • right = 9
  • mid = 7

图三
∵ n u m s [ 7 ] = 73 , t a r g e t = 65 ∴ n u m s [ 7 ] > t a r g e t ∴ r i g h t = m i d − 1 = 6 \begin{align*} &\because nums[7]=73,target=65 \\ &\therefore nums[7]>target \\ &\therefore right=mid-1=6 \end{align*} nums[7]=73,target=65nums[7]>targetright=mid1=6


  • left = 5
  • right = 6
  • mid = 5

图四
∵ n u m s [ 5 ] = 65 , t a r g e t = 65 ∴ n u m s [ 5 ] = t a r g e t ∴ r e t u r n    5 \begin{align*} &\because nums[5]=65,target=65 \\ &\therefore nums[5]=target \\ &\therefore return \thickspace 5 \end{align*} nums[5]=65,target=65nums[5]=targetreturn5


理解了二分查找的思想,我们就能完成代码了。

/* 
BinarySearch()实现二分查找
参数:nums -- 数组地址;nums_size -- 数组长度;target -- 目标值
返回:若能找到target,返回对于的下标;若不能,返回-1。
*/
int BinarySearch(int* nums, int nums_size, int target)
{
	int left, right, mid;
	
	left = 0;                               // 第一个数组元素的下标
	right = nums_size - 1;                  // 最后一个数组元素的下标
	while (left <= right)
	{
		mid = left + (right - left) / 2;    // 防止溢出int型
		if (nums[mid] > target)             // target位于左区间
			right = mid - 1;
		else if (nums[mid] < target)        // target位于右区间
			left = mid + 1;
		else if (nums[mid] == target)       // 找到target
			return mid;
	}

	return -1;
}

值得注意的是,我们将mid的表达式做了一些调整。
原来
m i d = l e f t + r i g h t 2 mid=\frac{left+right}{2} mid=2left+right
现在
m i d = l e f t + r i g h t − l e f t 2 mid=left+\frac{right-left}{2} mid=left+2rightleft
原因leftrightmid都是int型,它们之间的数学运算结果也是int型。假如数组很大,那么leftright的值也可能会很大。因此, l e f t + r i g h t left+right left+right的结果可能会超出int的最大值,发生溢出
考虑到溢出的风险,我们修改了表达式,数学运算的结果依然不变。同时 r i g h t − l e f t right-left rightleft的结果必然小于int的最大值,避免了溢出的风险。


解决题目

现在,我们回到这道题目上来。如果说targetnums中存在,那么与二分查找一样。但是,如果不存在,题目要求我们返回插入位置的下标。
我们将部分代码进行修改:
原代码:

if (nums[mid] > target)             
	right = mid - 1;
else if (nums[mid] < target)        
	left = mid + 1;
else if (nums[mid] == target)
	return mid;

修改后:

if (nums[mid] >= target)
	right = mid - 1;
else
	left = mid + 1;

修改后,无论nums中是否存在target,程序都会运行到 l e f t + 1 = r i g h t left+1=right left+1=right这一步,即leftright相邻。

  • target存在
    仍以上面二分查找的题目为例,target仍为65
    图五
    程序继续运行,left最终会来到65。此时,left的值即为所求。
  • target不存在
    target改为66
    图六
    程序继续运行,left的值要加一,即target插入位置的下标。此时,left的值也为所求。

总结:无论target是否在数组中,最终left的值都能满足要求。所以,我们最终返回left


完整代码

int searchInsert(int* nums, int numsSize, int target){
    int l, r, mid;

    l = 0; 
    r = numsSize - 1;
    while (l <= r)
    {
        mid = l + (r - l) / 2;
        if (nums[mid] >= target)
            r = mid - 1;
        else
            l = mid + 1;
    }

    return l;
}

(完)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值