微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

LeetCode刷题_第二天_二分查找_34_在排序数组中查找元素的第一个和最后一个位置

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

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

进阶:你可以设计并实现时间复杂度为 O ( log ⁡ n ) O(\log n) 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]

思路1:暴力求解法

  • 利用数组有序的特点从头到尾遍历一次数组;
  • 在遍历的开始,检查遍历到的元素是否等于target,遇到刚好等于target的时候,记录当前的位置;
  • 接着遍历,检查遍历到的元素是否不等于target,遇到刚好不等于target的时候,记录当前位置的前一个位置即可。
复杂度分析
  • 时间复杂度: O ( n ) O( n) O(n),这里的 n n n 为数组长度。
  • 空间复杂度: O ( 1 ) O( 1) O(1),只用到常数个临时变量。

思路2:二分查找法

  • 二分查找的基本思想:在一个区间范围里看处在中间位置的元素的值nums[mid]与目标元素target的大小关系,进而决定目标值落在哪一个部分里;
  • 目标元素target在有序数组中很可能存在多个;
  • 使用二分查找方法看到的处在中间位置的元素的值nums[mid]恰好等于目标元素target的时候,此时比较容易陷入的误区是线性查找而不使用二分查找,如果使用线性查找,将会使得最终的时间复杂度错误

一次二分查找一次只能查找出来一个索引,该题目需要使用两次二分查找,分别用来查找左边界和右边界,左边界在代码中是findFristPosition方法,右边界是findLastPosition方法

public class zuoyoubianjie {
    public static void main(String[] args) {
        int[] arr = {5,7,7,8,8,10};
        int target = 8;
        zuoyoubianjie zyb = new zuoyoubianjie();
        int[] result = zyb.searchRange(arr,target);
        System.out.println(result);
    }
    public int[] searchRange(int[] nums, int target) {
        int len = nums.length;
        if (len == 0) {
            return new int[]{-1, -1};
        }
        int firstPosition = findFristPosition(nums, target);
        if (firstPosition == -1) {
            return new int[]{-1, -1};
        }
        int lastPosition = findLastPosition(nums, target);
        return new int[]{firstPosition, lastPosition};
    }

    private int findLastPosition(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        while (left < right) {
        // (left right ) /2 时使用向下取整,这样在最终left,right和mid中会无限循环,因此应该+1向上取整
            int mid = (left + right + 1)  / 2;
            if (nums[mid] < target) {
                //下一轮搜索区间是[mid +1,right]
                left = mid + 1;
            } else if (nums[mid] == target) {
            //由于选的时右边界,所以nums[mid] == target时右边界只可能在右边或者就是mid
                //下一轮搜索的区间是[mid,right]
                left = mid;
            } else {
                //nums[mid] > target
                //下一轮搜索的区间是[left,mid -1]
                right = mid - 1;
            }
        }
        return left;
    }

    private int findFristPosition(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        //使用left <=right会使程序无限循环
        while (left < right) {
            int mid = (left + right)  / 2;
            if (nums[mid] < target) {
                //下一轮的搜索区间是[mid + 1,right]
                left = mid + 1;
            } else if (nums[mid] == target) {
            //由于选的时左边界,所以nums[mid] == target时左边界只可能在左边或者就是mid
                //下一轮搜索区间是[left, mid]
                right = mid;
            } else {
                //nums[mid]>target
                //下一轮的搜索区间是[left, mid-1]
                right = mid - 1;
            }
        }
        if (nums[left] == target) {
            return left;
        }
        return -1;
    }
}

原文地址:https://www.jb51.cc/wenti/3286727.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐