每日一练——第1天:在排序数组中查找元素的第一个和最后一个位置(Java语言) LeetCode34

题目来源:每日一练 熟能生巧——第1天:在排序数组中查找元素的第一个和最后一个位置(Java语言)
同时也是:LeetCode 34. 在排序数组中查找元素的第一个和最后一个位置


题目

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

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

进阶:你可以设计并实现时间复杂度为O(logn) 的算法解决此问题吗?

示例 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,第一次出现等于target时就记录下起始的位置,直到遍历到第一个大于target的元素为止,记录下其位置的前一个位置即可。但是此时的时间复杂度显然为O(n),不符合题目中的要求。
  • 二分查找:题目的关键字为数组有序,且要求时间复杂度为O(logn),所以尝试采用二分查找去解决问题。二分查找就是在一个有序的数组里,比较数组中间的那个元素与target的大小关系,从而确定target在哪一个区域里,一直二分下去,直到确定target的位置。
  • 题目的难点就在于target在这个有序数组中可能存在多个,找到符合要求的target的数组位置,还需要继续查找下去。那么如何继续查找下去呢?可以继续二分查找,此时可以分三种情况考虑
    • target>nums[mid]时,target就应该在数组的[mid+1,right]部分继续查找
    • target<nums[mid]时,target就应该在数组的[left,mid-1]部分继续查找
    • 但是当target==nums[mid]时,如果你是要找第一个出现的target的位置,那么target就应该在数组的[left,mid]部分继续查找;如果你是要找最后一个出现target的位置,那么target就应该在数组的[mid,right]部分继续查找

代码实现

public class SerachTarget {
    
    

    public static void main(String[] args) {
    
    
        Solution solution = new SerachTarget().new Solution();
        // to test
        int[] nums = {
    
    5,7,7,8,8,10};
        int target = 8;
        int[] result = new int[2];
        result = solution.searchRange(nums, target);
        System.out.println("[" + result[0] + "," + result[1] + "]");
    }
    public class Solution {
    
    
        public int[] searchRange(int[] nums, int target) {
    
    
            int len = nums.length;
            if(len == 0) {
    
      // 特殊情况:数组中没有元素
                return new int[]{
    
    -1,-1};
            }
            int firstPosition = floor(nums, target); // 查找第一个target出现的位置
            if(firstPosition == -1) {
    
       // 找不到第一个target出现的位置
                return new int[]{
    
    -1,-1};
            }
            int lastPosition = ceil(nums, target);  // 查找最后一个target出现的位置
            return new int[]{
    
    firstPosition, lastPosition};
        }
        // 查找第一个target出现的位置
        private int floor(int[] nums, int target) {
    
    
            int left = 0;
            int right = nums.length - 1;
            while (left < right) {
    
    
                int mid = (left + right) >>> 1;
                if (target < nums[mid]) {
    
               // 查找区间为[left,mid-1]
                    right = mid - 1;
                } else if(target == nums[mid]){
    
         // 查找区间为[left,mid]
                    right = mid;
                } else {
    
                                // target > nums[mid],查找区间为[mid+1,right]
                    left = mid + 1;
                }
            }
            if(nums[left] == target) {
    
      // 判断能否找到target
                return left;
            }
            return -1;
        }
        // 查找最后一个target出现的位置
        private int ceil(int[] nums, int target) {
    
    
            int left = 0;
            int right = nums.length - 1;
            while (left < right) {
    
    
                int mid = (left + right + 1) >>> 1;
                if (target > nums[mid]) {
    
               // 查找区间为[mid+1,right]
                    left = mid + 1;
                } else if(target == nums[mid]){
    
         // 查找区间为[mid,right]
                    left = mid;
                } else {
    
                                // target < nums[mid],查找区间为[left,mid-1]
                    right = mid - 1;
                }
            }
            return left;
        }
    }
}

作为一只菜鸟,正在努力学习中,希望大家多多给我提建议或者指点我,感激不尽!

Guess you like

Origin blog.csdn.net/Portia356/article/details/121802844