ts算法题解(第36天)----leetcode.34. 在排序数组中查找元素的第一个和最后一个位置

206 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情

前言

每天一道算法题,死磕算法

今天继续深入二分搜索,我们来做一下34. 在排序数组中查找元素的第一个和最后一个位置这道题目

题目

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

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

进阶:

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

 

示例 1:

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

分析

题目中说,你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?意思很明白就是让我们使用二分搜索算法

因为有重复值的原因,所以我们不能再使用昨天的那种搜索一个元素的方法

我们一贯秉承着模板大法好的原则,于是先上获取左边界的模板

function leftBound(nums: number[], target: number):number {
  let left = 0;
  let right = nums.length - 1;
  while (left <= right) {
    let mid = left + Math.floor((right - left) / 2);
    // 相等不返回,而是在减小区间
    if (nums[mid] === target) {
      right = mid - 1;
    } else if (nums[mid] < target) {
      left = mid + 1;
    } else if (nums[mid] > target) {
      right = mid - 1;
    }
  }
  // 查询左边界判断左边界
  if (left > nums.length || nums[left] !== target) {
    return -1;
  }
  // 查询左边界返回左
  return left;
}

关键点一:

 if (nums[mid] === target) {
      right = mid - 1;
    }

nums[mid] === target的时候,我们不再返回值,而是减小right的值,直到减小right的值在target的左侧的时候,此时由于没有目标值,while最后会结束,由于结束的条件是left===right+1;所以我们返回left就可以拿到左侧的目标值;

关键点二:

if (left >nums.length-1 || nums[left] !== target) {
    return -1;
  }

这是用来判断没有目标值返回-1的情况,因为我们最后返回的是left;那么我们就可以判断nums[left]!==target的时候,就返回-1,但是有可能left会越界,因为left最小为0,所以我们只需要判断left >nums.length-1 那就是越界了,此时返回-1。

获取右边界的模板和左边界的模板大同小异

function right_bound(nums:number[],target:number):number{
  let left = 0;
  let right = nums.length-1;
  while(left<=right){
      let mid = left + Math.floor((right-left)/2);
    if (nums[mid] === target) {
          left = mid + 1;
      }else if(nums[mid]<target){
          left = mid + 1;
      }else if(nums[mid]>target){
          right = mid - 1;
      }
  }
  // 查询右边界看右边界是否越界
  if (right < 0 || nums[right] !== target) {
    return -1;
  }
  // 查询右边界返回右
  return right;
}

关键点一:

 if (nums[mid] === target) {
          left = mid + 1;
      }

nums[mid] === target的时候,我们不在返回值,而是增大left的值,直到增大到left在target的右侧的时候,此时由于没有目标值,while最后会结束,由于结束的条件是left===right+1;所以我们返回right就可以拿到左侧的目标值;

关键点二:

 if (right < 0 || nums[right] !== target) {
    return -1;
  }

这是用来判断没有目标值返回-1的情况,因为我们最后返回的是right;那么我们就可以判断nums[right]!==target的时候,就返回-1,但是有可能right会越界,因为right最大为nums.length-1,所以我们只需要判断right<0那就是越界了,此时返回-1。

题解

function left_bound(nums: number[], target: number):number {
  let left = 0;
  let right = nums.length - 1;
  while (left <= right) {
    let mid = left + Math.floor((right - left) / 2);
    // 相等不返回,而是在减小区间
    if (nums[mid] === target) {
      right = mid - 1;
    } else if (nums[mid] < target) {
      left = mid + 1;
    } else if (nums[mid] > target) {
      right = mid - 1;
    }
  }
  // 查询左边界判断左边界
  if (left > nums.length-1 || nums[left] !== target) {
    return -1;
  }
  // 查询左边界返回左
  return left;
}

function right_bound(nums:number[],target:number):number{
  let left = 0;
  let right = nums.length-1;
  while(left<=right){
      let mid = left + Math.floor((right-left)/2);
    if (nums[mid] === target) {
          left = mid + 1;
      }else if(nums[mid]<target){
          left = mid + 1;
      }else if(nums[mid]>target){
          right = mid - 1;
      }
  }
  // 查询右边界看右边界是否越界
  if (right < 0 || nums[right] !== target) {
    return -1;
  }
  // 查询右边界返回右
  return right;
}


function searchRange(nums: number[], target: number): number[] {
    let leftBound = left_bound(nums,target);
    let rightBound = right_bound(nums,target);
    return [leftBound,rightBound];
};

总结

为了方便记忆,我们依然拿多才多艺的东哥的诗来结束本篇文章

搜索左右区间时,搜索区间要阐明。
if相等别返回,利用mid锁边界。
while结束不算完,因为你还没返回。
索引可能出边界,if检查保平安。

参考