34.在排序数组中查找元素的第一个和最后一个位置.|Java刷题打卡

300 阅读2分钟

本文正在参加「Java主题月 - Java 刷题打卡」,详情查看 活动链接

题目描述

来源:力扣(LeetCode)

链接:leetcode-cn.com/problems/fi…

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

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

进阶:

  • 你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?

示例 1:

输入:nums = [5,7,7,8,8,10], target = 8

输出:[3,4]

示例 2:

输入:nums = [5,7,7,8,8,10], target = 6

输出:[-1,-1]

示例 3:

输入:nums = [], target = 0

输出:[-1,-1]

提示:

  • 0 <= nums.length <= 105
  • -109 <= nums[i] <= 109
  • nums 是一个非递减数组
  • -109 <= target <= 109

题目分析

一般解法

话不多说,直接就从左到右遍历一遍,先找到第一个与target的元素,再找第二个

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int[] result = {-1,-1};
        int len = nums.length;
        if(len == 0 || nums[0] > target || nums[len - 1] < target){
            return result;
        }

        boolean flag = true;
        for (int i = 0; i < len; i++) {
            if (nums[i] == target){
                if(flag){
                    result[0] = i;
                    flag = false;
                }
            }
            if(!flag && nums[i] != target) {
                result[1] = i - 1;
                break;
            }
        }

        if((len == 1 && nums[0] == target)){
            result[1] = 0;
        }

        if(nums[len - 1] == target) {
            result[1] = len - 1;
        }
        return result;
    }
}

时间复杂度为O(n),然后挑战一下复杂度为 O(log n) 的解法

二分查找

  • 有序和数组这个两个字眼结合起来,肯定是要用到二分查找

  • 首先就是找最左侧的下标,利用二分查找首先是找到有一个值是与目标值target是相等的,然后因为是找最左侧的下标,所以把right=mid-1来一直往左边去逼近最左侧的值

  • 至于找最右侧的下标就是,将left=mid+1,来去逼近最右侧的下标

  • 如果没有找到则说明不存在返回-1

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int [] result = {-1,-1};
        result[0] = findLeftIndex(nums,target);
        result[1] = findRightIndex(nums,0,target);
        return result;
    }
    public int findLeftIndex(int [] nums,int target)
    {
        int left = 0;
        int right = nums.length - 1;
        int leftIndex = -1;
        while(left <= right)
        {
            int mid = left + (right - left) / 2;
            if(nums[mid] < target)
            {
                left = mid + 1;
            }else if(nums[mid] > target)
            {
                right = mid - 1;
            }else{
                leftIndex = mid;
                right = mid - 1;
            }
        }
        return leftIndex;
    }
    public int findRightIndex(int [] nums,int left,int target)
    {

        int right = nums.length - 1;
        int rightIndex = -1;
        while(left <= right)
        {
            int mid = left + (right - left) / 2;
            if(nums[mid] < target)
            {
                left = mid + 1;
            }else if(nums[mid] > target)
            {
                right = mid - 1;
            }else{
                rightIndex = mid;
                left = mid + 1;
            }
        }
        return rightIndex;
    }
}

总结

这种解法思路还是很清晰的,只是一些边界条件需要判断。二分查找的时间复杂度为 O(\log n)

这里简单写一下二分查找,比如对于数组[1,2,4,4,4,4,4,5,6],找4的最左下标。

  • 对于这个数目来说,left,right,mid分别代表下标值首先left=0,right=8,所以mid=(0+8)/2 = 4;

  • 由于target=4与nums[mid]相等,所以此时记录下来这个下标,也就是mid的值4,这个下标是可能的最左的4的下标所以要记录保存一下;

  • 观察这个数组,可以知道,最左的4的下标是2,所以为了找到这个最左的下标,需要令right的值去等于mid-1;这样就把right这一边慢慢地往左靠,因为是找最左的嘛,所以肯定是要缩小right的的值去逼近这个最左的4,直到找到这个最左的4为止;

  • 找最右边的4的思路也是一样的,就是令left=mid+1去逼近最右边的这个4.