数组二分查找算法

要求:已知一组从小到大排好序的有序数组nums,从里面找一个目标值target,如果nums数组存在target,则返回target在nums数组的下标,否则返回target将会插入到数组nums哪个位置的下标.有两种情况比较常见,第一种在nums数组里左闭右闭区间( [left,right] )里找目标值target;第二种是在nums数组里左闭右开区( [left,right) )间里找目标值target.(这两种情况的不同在于查找的区间不同,区别是二分查找法的执行前提不一样,跟右指针right移动到的位置不一样.)

思路:

用二分查找法.

二分查找法:

  1. 该操作的执行前提是left<=right(左闭右闭区间)或者left<right(左闭右开区间).二指的是有两个可移动指针指向数组nums,左指针left跟右指针right.left指向数组nums区间的最左端,right指向数组nums区间的最右端.分指的是把数组nums分成两半,并把指针middle指向数组nums的中间位置(middle=(left+right)\2);

  1. 然后判断数组nums的中间位置的值nums[middle]与目标值target的大小关系.移动left或者right指针或者返回target下标.如果nums[middle]<target,则左指针向右移,移到middle位置;如果nums[middle]=target,则返回middle下标,如果nums[middle]>target,则右指针向左移,移到middle(左闭右开情况)或者middle-1(左开右闭情况)位置;

  • 原因:

  • 第一种情况,[left,right](左闭右闭);由于数组nums的中间位置的值nums[middle]与目标值target的大小关系(大于小于)已比较过,已经确定了nums[middle]!=target,又因为是左闭右闭区间,更新后的区间也是左闭右闭区间(右闭说明此区间有包括右这个数,由于右num[middle]这个数已经跟target比较过了,并不相等),所以相应指针应该移到middle的上一个位置,更新区间[left,right]的范围,再重复之前的操作,前提是left<=right.(因为是左闭右闭区间,所以left<=right是合法的;比如要在[1,1]这个区间里寻找target数组,明显是这个区间是有存在1个数的,是可以进行判断target在不在这个区间里的);

  • 第二种情况,[left,right)(左闭右开);由于数组nums的中间位置的值nums[middle]与目标值target的大小关系(大于小于)已比较过,已经确定了nums[middle]!=target,又因为是左闭右开区间,更新后的区间也是左闭右开区间(右开说明此区间没有包括右这个数),所以相应要移动的指针应该移动到middle这个位置即可.接下来更新区间[left,right]的范围,再重复之前的操作,前提是left<right.(因为我们观察的区间一直是左闭右开区间,只有left<right是合法的,不能left=right.比如[1,2)是可以的,但是[1,1)是不对的,因为该区间里面没有数)

  1. 如果target不在nums数组里,则二分法会执行到left>right,不满足二分法的执行条件,会返回right+1.

原因:

  • 第一种情况:目标值target在数组所有元素之前,target永远<nums[middle],二分法会执行到区间为[0, -1],此时不满足left<=right,应该return right + 1(=0),target插入到0的位置.

  • 第二种情况: 目标值在数组所有元素之后的情况,在我们查找的范围 [left, right]之后,二分法会执行到[right,right+1],此时不满足left<=right,应该return right + 1,target插入到最后的位置

代码:

int searchInsert(vector<int>& nums, int target) {
        int n = nums.size();//nums数组的长度 
        int left = 0;//nums数组下标从0开始
        int right = n - 1; // nums数组最后一个数的下标 
        while (left <= right) { // 二分法执行的条件 
            int middle = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
            if (nums[middle] > target) {
                right = middle - 1; // right指针向左移动,移到middle-1的位置.因为是右闭区间,又因为nums[middle]已经与target比较过大小了,right指针无需指到middle位置再进行数据与target比较了
            } else if (nums[middle] < target) {
                left = middle + 1; // left指针向右移动,移动到middle+1位置 .因为是左闭区间,又因为nums[middle]已经与target比较过大小了,left指针无需指到middle位置再进行数据与target比较了
            } else { // nums[middle] == target,找到target值 
                return middle;//返回下标 
            }
        }
        return right + 1;/*目标值target在数组所有元素之前,target永远<nums[middle],二分法会执行到区间为[0, -1],
                          此时不满足left<=right,应该return right + 1(=0),target插入到0的位置. 
                          目标值在数组所有元素之后的情况,在我们查找的范围 [left, right]之后,二分法会执行到[right,right+1], 
                          此时不满足left<=right,应该return right + 1,target插入到最后的位置 
                        */            
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值