【力扣】34. 在排序数组中查找元素的第一个和最后一个位置

二分查找法

力扣链接:34. 在排序数组中查找元素的第一个和最后一个位置

注意事项:

  1. 当考虑查找元素的边界如0(数组没有元素),代码块需要判断length-1合法可以利用这个下标取出数组中的值,当数组为空时,会出现数组溢出的情况。
  2. 在计算数组中间值的时候,为了防止数组溢出 使用的是middle = left + ((right - left) >> 1),使用>>表示右移 比% /的效率高。

方法一

这道题先使用传统的二分算法 查找中间的middle值,有两种可能:

  1. 第一种是找到了,那么接下来从中间开始查找左右区间,从middle往前或者往后移动(使用两个变量可以使用之前使用的两个left和right变量)但是这里因为移动变量后续通过变量取数组中的数值,所以需要考虑变量溢出的情况,即left和right和0和length-1的关系,如果溢出则break
  2. 第二种没有找到 则返回[-1,-1]

代码如下:

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int[] result = {-1, -1};
        if(nums.length==0 ||  target < nums[0] ||target > nums[nums.length - 1] ){
            return result;
        }
        int left = 0;
        int right = nums.length - 1;
        int middle = 0;
        while(left <= right){
            middle = left + ((right - left) >> 1);
            if(nums[middle] > target){
                right = middle - 1;
            }else if(nums[middle] < target){
                left = middle + 1;
            }else{
                break;
            }
        }
        if(left > right){
            return result;
        }
        left = middle;
        right = middle;
        while(nums[left] == target){
            left -= 1;
            if(left < 0 ){
                break;
            }
        }
        while(nums[right] == target){
            right += 1;
            if(right > nums.length - 1){
                break;
            }
        }
        result[0] = left + 1;
        result[1] = right - 1;
        return result;
    }
}

方法二

首先需要先考虑3种情况:

  1. 第一种,即不存在目标元素:目标元素不存在数组中(这里利用找到的边界的差等于1 进行排除)
  2. 第二种,即目标元素在其中(通过找到的边界差大于1 进行获取)
  3. 第三种 数组里面元素为空;目标元素和下标边界的直接对比小于0,大于length-1 :通过未赋值的边界进行处理[-2, -2]

根据以上三种情况,写算法,如何确定上下边界呢?通过修改二分查找中的相等条件,使用left确定上边界,即相等的时候移动left;使用right确定下边界,即相等的时候移动right。

这里考虑了特殊情况,当目标元素小于nums[0]时,当确定上边界时,需要移动left,而left移动的条件是target更大,所以不可能被赋值,同理目标元素大于nums[nums.length-1]时,确定下边界,需要更新right,而right的值更新是小于某个值,所以下边界不能被确认。

区分情况一和情况二,通过赋值之后的元素差进行区分,如果相差为1,则说明不存在目标元素,反之则存在。

因为下边界是找到目标数组中第一小于目标元素的值(小于目标元素的最大值),同理上界也是,所以可以通过下标确定元素是否存在,不存在,则小边界和上边界连续。

代码如下

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int[] result = {-1, -1};
        // 这里先判断length==0,如果移动了顺序 那么后面会报错(数组溢出问题)
        // 通过这几种判断 解决情况三
        if(nums.length==0 ||  target < nums[0] ||target > nums[nums.length - 1] ){
            return result;
        }
        int rightBorder = getRightBorder(nums, target);
        int leftBorder = getLeftBorder(nums, target);
        // 这里只需要直接判断找到了的情况
        if(rightBorder - leftBorder > 1 ){
            result[0] = leftBorder + 1;
            result[1] = rightBorder - 1;
            return result;
        }
        // 没有目标元素 即是第一种情况,不用再进行判断了
        return result;
    }

    int getRightBorder(int[] nums, int target){
        int left = 0;
        int right = nums.length - 1;
        // 上边界 通过移动left获取,所以相等的情况是left移动
        int rightBorder = -2;
        int middle ;
        while(left <= right){
            middle = left + ((right - left) >> 1);
            if(nums[middle] > target){
                right = middle - 1;
            }else{
                left = middle + 1;
                // 最终上边界是目标上边界+1 因当相等时,需要再往上移动一位
                rightBorder = left;
            }
        }
        return rightBorder;
    }
    int getLeftBorder(int[] nums, int target){
        int left = 0;
        int right = nums.length - 1;
        // 下边界 通过移动right获取,所以相等的情况是right移动
        int leftBorder = -2;
        int middle;
        while(left <= right){
            middle = left + ((right - left) >> 1);
            // 相等时 是right需要移动
            if(nums[middle] >= target){
                right = middle - 1;
                // 最终下边界是目标下边界-1 因当相等时,需要再往下移动一位
                leftBorder = right;
            }else{
                left = middle + 1;
            }
        }
        return leftBorder;
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值