zl程序教程

您现在的位置是:首页 >  其他

当前栏目

LeetCode高频题34. 在排序数组中查找元素的第一个和最后一个位置

LeetCode数组排序 一个 元素 查找 位置 第一个
2023-09-11 14:15:38 时间

LeetCode高频题34. 在排序数组中查找元素的第一个和最后一个位置

提示:本题是系列LeetCode的150道高频题,你未来遇到的互联网大厂的笔试和面试考题,基本都是从这上面改编而来的题目
互联网大厂们在公司养了一大批ACM竞赛的大佬们,吃完饭就是设计考题,然后去考应聘人员,你要做的就是学基础树结构与算法,然后打通任督二脉,以应对波云诡谲的大厂笔试面试题!
你要是不扎实学习数据结构与算法,好好动手手撕代码,锻炼解题能力,你可能会在笔试面试过程中,连题目都看不懂!比如华为,字节啥的,足够让你读不懂题
在这里插入图片描述


题目

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

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

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

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。


一、审题

示例 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


二、解题

比如arr中找6
6的开头结尾位置6–8
找不到-1,-1
在这里插入图片描述

算法原型:在有序数组中,二分查找>=x,最左位置,<=x最右位置

在有序数组arr中
找>=x最左的位置【满足条件,记录当前位置i,使劲往左搜】
如果找到中间位置[M]<x,说明x在M右边,下次让L=M+1
如果找到中间位置[M]>=x,可能M左边还有>=x的呢,先记下当前达标位置i=M,下次让R=M-1
直到L>R停止搜索

在这里插入图片描述
手撕代码:

        //算法原型:
        //找>=x最左的位置【满足条件,记录当前位置i,使劲往左搜】
        //如果找到中间位置[M]<x,说明x在M右边,下次让L=M+1
        //如果找到中间位置[M]>=x,可能M左边还有>=x的呢,先记下当前达标位置i=M,下次让R=M-1
        //直到L>R停止搜索
        public static int findBigEqual(int[] arr, int L, int R, int x){
            int i = -1;//默认没有
            while (L <= R){
                int M = L + ((R - L) >> 1);
                if (arr[M] < x) L = M + 1;
                else {
                    i = M;
                    R = M - 1;//使劲往左找
                }
            }

            return i;
        }

        

找<=x最右的位置【满足条件,记录当前位置j,使劲往右搜】
可以二分查找
如果找到中间位置[M]<=x,可能M右边还有<=x的呢,先记下当前达标位置j=M,下次让L=M+1
如果找到中间位置[M]>x,说明x在M左边,下次让R=M-1
直到L>R停止搜索
在这里插入图片描述
手撕代码:

//找<=x最右的位置【满足条件,记录当前位置j,使劲往右搜】
        //可以二分查找
        //如果找到中间位置[M]<=x,可能M右边还有<=x的呢,先记下当前达标位置j=M,下次让L=M+1
        //如果找到中间位置[M]>x,说明x在M左边,下次让R=M-1
        //直到L>R停止搜索
        public static int findLessEqual(int[] arr, int L, int R, int x){
            int i = -1;//默认没有
            while (L <= R){
                int M = L + ((R - L) >> 1);
                if (arr[M] > x) R = M - 1;
                else{
                    i = M;
                    L = M + 1;//使劲往右找
                }
            }

            return i;
        }

本题就利用上面这个算法原型,这还是非常非常简单的

比如找target=6
(1)则找<=6的最右的位置R
(2)找>=6最左的位置L

看看LR不是-1,就是找到了,再看L<=R而且L处是target,R处也是target的话,绝对完美L–R就是结果
否则就没有
手撕代码



        public int[] searchRangeReview(int[] nums, int target) {
            //比如找target=6
            if (nums == null || nums.length == 0) return new int[] {-1, -1};

            int N = nums.length;
            int[] ans = new int[2];//0左边界,1右边界
            ans[0] = -1;
            ans[1] = -1;//,默认没有

            //(2)找>=6最右的位置R
            int L = findBigEqual(nums, 0, N - 1,target);
            //(1)则找<=6的最右的位置i
            int R = findLessEqual(nums, 0, N - 1, target);

            //反正L和R要么同时为-1,要么不会为-1
            //你看看ij处是6吗?是就是j--i,//否则就是i-1,j+1
            if (L != -1 && R != -1){
                ans[0] = L <= R && nums[L] == target ? L : -1;
                ans[1] = L <= R && nums[R] == target ? R : -1;
            }

            return ans;
        }

绝对完美
测试一把:

    public static void test(){
        int[] arr = {5,7,7,8,8,10};
        int target = 7;
        Solution solution = new Solution();
        int[] res = solution.searchRange(arr, target);
        System.out.print(res[0] + " " + res[1]);
        System.out.println();

        res = solution.searchRangeReview(arr, target);
        System.out.print(res[0] + " " + res[1]);
    }

    public static void main(String[] args) {
        test();
    }
1 2
1 2

LeetCode测试:
在这里插入图片描述
绝对碉堡
在这里插入图片描述


总结

提示:重要经验:

1)算法原型:在有序数组中,二分查找>=x,最左位置,<=x最右位置,使劲朝着一边找,之前记录已经找到的位置i
2)比如找target(1)则找<=6的最右的位置R,(2)找>=6最左的位置L,如果LR不是-1,必然L<=R,必然就是找到了的
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。