【力扣刷题记 34】——《在排序数组中查找元素的第一个和最后一个位置》

86 阅读1分钟

Offer 驾到,掘友接招!我正在参与 2022 春招打卡活动,点击查看活动详情

一、题目描述:

  1. 在排序数组中查找元素的第一个和最后一个位置 —— 难度中等

给定一个按照升序排列的整数数组 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 <= 10^5

-10^9 <= nums[i] <= 10^9

nums 是一个非递减数组

-10^9 <= target <= 10^9

二、题目和思路分析:

这这这,和上一题有什么区别吗?

为什么会有这种奇奇怪怪的题目啊!!!

我直接一个return [nums.indexOf(target), nums.lastIndexOf(target)]解决问题啊啊啊!

image.png

可是我想了想,这道题大概也是要考察时间复杂度,于是我大手一挥,把双指针的代码改了一下。

不过改的过程中遇到了很多问题,比如各种情况的考虑不周到,毕竟查找两个下标比查找一个下标复杂了一倍,最后写了半天总算通过了。

image.png

可是这用时,这内存,不禁让我怀疑自己的思路是否正确。

那么是否有更简单的方法呢?

我想了又想,那大概是二分法了。

因为数组是有序的,二分法可以快速的定位到目标值事都存在于当前的二分数组中,直接循环遍历判断即可。

三、代码:

代码实现如下:


/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var searchRange = function(nums, target) {
    let left = 0
    let right = nums.length-1
    let arr = [-1, -1]
    while (left <= right) {
        let mid = left + right >> 1
        if (nums[mid] == target) {
            arr[0] = arr[1] = mid
            while(arr[0] > left && nums[arr[0] - 1] == target){
                arr[0]--
            }
            while(arr[1] < right && nums[arr[1] + 1] == target){
                arr[1]++
            }
            break
        }else if (nums[mid] < target){ // 继续往右找
            left = mid + 1
        }else{ // 继续往左找
            right = mid - 1
        }
    }
    return arr
};

四、总结:

做了上一道题,我再看这道题就觉得更加简单了,想着这道题其实应该被归到简单类型里面。实际上我在这道题上使用双指针的解法时浪费了很多时间,主要是细节没把握好。双指针加双下标确实比较乱,容易有想不到的地方。

改成二分法之后,整体逻辑变得更加明了简单,速度也提升了近5倍,早早放弃双指针遍历才对。

加油吧!

image.png