leetcode35.搜索插入位置

1)题目描述:

2)本题要求使用 时间复杂度O(log n)的算法,这里使用二分查找的方法,这道题本身不复杂,但是,在使用递归调用时,笔者经常把递归结束的边界搞错,这里给出几版代码,做一下讨论

第1版代码:

class Solution {
public:
    int findMid(vector<int>& nums, int l, int r, int target) {
        int mid = (l+r)/2;
        if(nums[mid] == target) {
            return mid;
        }
        else if(l == r) {
            if(nums[mid] < target) {
                return mid+1;
            }
            else {
                return mid;
            }
        }
        else if(l > r) {
            // array is [3, 5, 7, 9, 10], target=8
            // ...
            // l=3, r=4  m=3   nums[mid]>target
            // l=3, r=2  m=2
            // when l > r, just return l
            return l;
        }
        else {
            if(nums[mid] < target) {
                l = mid + 1;
            }
            else {
                r = mid - 1;
            }
        }
        return findMid(nums, l, r, target);
    }

    int searchInsert(vector<int>& nums, int target) {
        return findMid(nums, 0, nums.size()-1, target);
    }
};

这里需要注意的一点是,如果需要继续二分查找,则需要更新左右边界,笔者直觉上认为,如果nums[mid] < target,将左边界更新为mid+1,如果nums[mid] > target,将右边界更新为mid-1,但是在实际执行程序时,这样做可能会出现左边界>右边界的情况,程序进入无限循环,如下图所示:

所以在原始代码的基础上增加了对于"左边界>右边界的情况"的简单处理,当然了,之所以可以简单处理,是因为出现这种情况时,搜索插入位置可以确定了。

第2版代码:

class Solution {
public:
    int findMid(vector<int>& nums, int l, int r, int target) {
        int mid = (l+r)/2;
        if(nums[mid] == target) {
            return mid;
        }
        else if(l == r) {
            if(nums[mid] < target) {
                return mid+1;
            }
            else {
                return mid;
            }
        }
        else {
            if(nums[mid] < target) {
                l = mid + 1;
            }
            else {
                r = mid;
            }
        }
        return findMid(nums, l, r, target);
    }

    int searchInsert(vector<int>& nums, int target) {
        return findMid(nums, 0, nums.size()-1, target);
    }
};

在第1版代码的基础上,笔者在想,是否可以避免"左边界>右边界的情况",同时为了要找到插入位置,还要不断地缩小搜索空间,在上面列举的出现"左边界>右边界的情况"的例子中,最后,l=3,r=4,m=3,最后nums[mid]>target,需要将r更新为mid-1,那么这里我们可以做保守处理,将r更新为mid。如果l与r相同,代码做了详尽处理。l=mid+1、r=mid-1的边界更新策略就是没有很好地处理l+1=r的情况,这里检查一下l=mid+1、r=mid的边界更新策略是否能处理r-l=1的情况。如果r-l=1,则mid=(l+l+1)/2=l,如果nums[mid]=target,则搜索插入位置是mid,如果nums[mid]<target,则l保持不变,r更新为mid(上一步的l),如果nums[mid]>target,则l更新为mid+1(上一步的l+1),r保持不变。如果r-l>1,在不断地缩小搜索空间后,总会进入到l=r或r-l=1的情况。

第3版代码:

class Solution {
public:
    int findMid(vector<int>& nums, int l, int r, int target) {
        int mid = (l+r)/2;
        if(mid*2+1 == l+r) {
            mid++;
        }
        if(nums[mid] == target) {
            return mid;
        }
        else if(l == r) {
            if(nums[mid] < target) {
                return mid+1;
            }
            else {
                return mid;
            }
        }
        else {
            if(nums[mid] < target) {
                l = mid;
            }
            else {
                r = mid - 1;
            }
        }
        return findMid(nums, l, r, target);
    }

    int searchInsert(vector<int>& nums, int target) {
        return findMid(nums, 0, nums.size()-1, target);
    }
};

这里将第3版代码与第2版做对比,讨论mid与左右边界的关系、以及左右边界的更新策略,当l+r不能整除时,(l+r)/2取下整,举个例子,如果l=3,r=4,则mid应该是3.5,当然计算机默认策略是取下整3,是小于3.5的一个整数,所以在r-l=1时,l与mid是重合的,如果是左边界更新,可以将其更新为mid+1,向右边界靠近,如果是右边界更新,只能将其更新为mid(如果更新为mid-1,则可能出现左边界>右边界的情况),也可以理解,mid=(l+r)/2可能在左右边界的中间位置,也有可能偏左。第3版代码就是在(l+r)不能整除2的情况下,让mid偏右,这时左右边界的更新策略可以改为l=mid,r=mid-1。

4)关于递归,要仔细考虑到自己的代码最后结束的情况有哪几种,这样才能避免未预期的情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值