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

题目

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例

输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]

题解

题目要求时间复杂度为O(log n)来解决此问题,所以首先排除了暴力解法,因为暴力解法的话,时间复杂度要到达O(n)

那么这道题就可以思考使用二分查找进行解决,为什么这么想呢?
大家都知道二分查找可以很快地从一个数组中,找到目标值。
可能不知道的是二分查找还能找目标值得边界。
怎么找呢?如下代码。

    public int rightBorder (int[] nums, int target){
        int left = 0, right = nums.length - 1;
        while (left <= right){
            int mid = left + ((right - left) >> 1);
            if (nums[mid] <= target){
                left = mid + 1;
            }else {
                right = mid - 1;
            }
        }
        return left;
    }

这段代码就可以从一个数组中找到目标值得右边界。
大家可以思考一下,当 if (nums[mid] <= target) 时 也就是左边界小于或等于目标值的话就一直往右移,那么当循环结束的时候,原先的左界就成了右边界

同理,可以找到左边界

        public int leftBorder (int[] nums, int target){
            int left = 0, right = nums.length - 1;
            while (left <= right){
                int mid = left + ((right - left) >> 1);
                if (nums[mid] < target){
                    left = mid + 1;
                }else {
                    right = mid - 1;
                }
            }
            return right;
        }

一级标题

整体代码

public int[] searchRange(int[] nums, int target) {
        int leftBor = leftBorder(nums, target);
        int rightBor = rightBorder(nums, target);

        if (leftBor > nums.length - 1 || rightBor < 0){
            return new int[]{-1,-1};
        }

        if (rightBor - leftBor < 2){
            return new int[]{-1,-1};
        }else {
            return new int[]{leftBor + 1, rightBor - 1};
        }
    }
        public int leftBorder (int[] nums, int target){
            int left = 0, right = nums.length - 1;
            while (left <= right){
                int mid = left + ((right - left) >> 1);
                if (nums[mid] < target){
                    left = mid + 1;
                }else {
                    right = mid - 1;
                }
            }
            return right;
        }
    public int rightBorder (int[] nums, int target){
        int left = 0, right = nums.length - 1;
        while (left <= right){
            int mid = left + ((right - left) >> 1);
            if (nums[mid] <= target){
                left = mid + 1;
            }else {
                right = mid - 1;
            }
        }
        return left;
    }

当我们找到左边界和右边界后,可以很容易得到答案 return new int[]{leftBor + 1, rightBor - 1};
但这里要警惕两种特殊情况,数组中没有目标值(这里分为两种情况,即目标值在数组包含范围内,或目标值在数组包含范围外)

数组中没有目标值(目标值在数组包含范围内)

这时右边界 - 左边界 应该是 < 2 的,因为就算只有一个目标值,那也是 = 2,所以

if (rightBor - leftBor < 2){
            return new int[]{-1,-1};
        }

数组中没有目标值(目标值在数组包含范围外)

这时一个边界会不动,一个边界会一直移动直至不能满足循环条件跳出,所以

        if (leftBor > nums.length - 1 || rightBor < 0){
            return new int[]{-1,-1};
        }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值