【二分查找】典型题

1 题1- LeetCode34- 在排序数组中查找元素的第一个和最后一个位置

1.1 实现思路

2 二分法的本质:二段性,即 要查找的数据是否可以分为两部分,前半部分为X,后半部分为O

  • 开始位置--> 第一个等于target的值--> 即 >=target区间A 的第一个元素
  • 结束位置--> 最后一个等于target的值--> 即 <=target区间A内 的最后一个元素

3 参考文档

01- 二分查找代码模板

02- 二分查找区间理解

1.2 代码实现

方法1: 二分查找------ 时间复杂度: O(logn); 空间复杂度: O(1)

ts 复制代码
function searchRange(nums, target) {
  let res = [-1, -1];
  if (nums.length === 0) return res;

  // 获取 第一个等于target的值
  let l1 = 0, r1 = nums.length;
  while (l1 < r1) {
    const mid = (l1 + r1) >> 1;
    // 此时中点值是>=target的,我们需要这部分的最左侧元素,所以截去右侧 部分
    if (nums[mid] >= target) {
      r1 = mid;
    } else {
      // 此时中点值是<target的,我们不需要<target的部分,所以截去左侧 小于的部分
      l1 = mid + 1;
    }
  }

  // 获取 最后一个等于target的值
  let l2 = 0, r2 = nums.length;
  while (l2 < r2) {
    // 易错点: 这里当 nums[mid] <= target且l2 = mid,来截去左侧部分时
    // mid应该是 (l2 + r2 + 1) >>1, 而不是 (l2 + r2) >>1
    // 考虑 [1,1], target = 1的 情况
    // 此时如果是(l2 + r2) >>1,那么 l2 = mid = 0
    // 会导致l2一直是0,从而导致死循环

    // 所以,只要截去左侧的操作是通过l = mid,而不是mid + 1来实现时
    // mid取值就要向上取整
    const mid = (l2 + r2 + 1) >> 1;
    // 此时中点值是<=target的,我们需要这部分的最右侧元素,所以截去左侧 部分
    if (nums[mid] <= target) {
      l2 = mid;
    } else {
      // 此时中点值是>target的,我们不需要>target的部分,所以截去右侧 大于的部分
      r2 = mid - 1;
    }
  }

  // 易错点2: 最后要看r1/r2位置的值 是否等于target,要考虑数组里不存在target的情况
  res[0] = nums[r1] === target ? r1 : -1;
  res[1] = nums[r2] === target ? r2 : -1;
  return res;
}

2 题2- LeetCode875-爱吃香蕉的珂珂

2.1 实现思路

1 关键特征:最值问题

2 方法1: 二分查找

二分查找技巧 适用场景

    1. 有明确的 搜索范围
    1. 搜索范围内成员 满足二段性==> 最值问题
    1. 通过【二分查找】不断缩减搜索范围, 最终确定答案

3 参考实现:
官方实现

2.2 代码实现

方法1: 二分查找------ 时间复杂度: O(nlogm); 空间复杂度: O(1)

ts 复制代码
function minEatingSpeed(piles: number[], h: number): number {
  let l = 1, r = Math.max(...piles);
  while (l < r) {
    let mid = l + ((r - l) >> 1);
    const needTime = calTime(piles, mid);
    // 求的是 <=h的 最大值
    // 如果需要的时间 <=h,说明吃的速度已经够快了,需要减慢速度
    if (needTime <= h) {
      r = mid;
    } else {
       // 反之 如果需要的时间 >h,说明吃的速度慢了,需要加快速度
      l = mid + 1;
    }
  }
  return r;
};

function calTime(piles, speed) {
  return piles.reduce((acc, cur) => acc + Math.ceil(cur / speed), 0);
}

题3- LeetCode1011- 在D天内送达包裹的能力

3.1 实现思路

1 最值问题/分段特性==> 二分查找

2 参考文档:

01- 利用「二段性」找分割点

02- 本题时间复杂度分析

3.2 代码实现

方法1: 二分查找法 时间复杂度: O(nlog(Σw)); 空间复杂度: O(1)

ts 复制代码
function shipWithinDays(weights: number[], days: number): number {
  // r = 30000000是 根据题目限制得出的: 500 * 50000 = 25000000
  let l = Math.max(...weights), r = 30000000;
  while (l < r) {
    const mid = l + ((r - l) >> 1);
    const needDays = calDays(weights, mid);
    // 期望求的是 <= days的最大值
    // 如果needDays <= days,说明运载能力还可以再小一点
    if (needDays <= days) {
      r = mid;
    } else {
      l = mid + 1;
    }
  }
  return r;
}

function calDays(weights, cap) {
  // 易错点: days 默认是从1开始计数的,而不是0
  let days = 1, curLoad = 0;
  for (let weight of weights) {
    if (curLoad + weight <= cap) {
      curLoad += weight;
    } else {
      days++;
      curLoad = weight;
    }
  }
  return days;
}
相关推荐
铁皮饭盒14 小时前
用bunjs代码讲解XSS/CSRF/SQL注入/DDos等10种前后端安全防护
前端·后端
叫我:松哥14 小时前
基于卷积神经网络的静态手势语识别算法,在测试集上的识别准确率达到97.5%
人工智能·python·深度学习·神经网络·算法·cnn
珊瑚里的鱼14 小时前
【动态规划】买卖股票的最佳时机含手续费
算法·动态规划
琹箐14 小时前
chrome 插件下载安装;Manifest file is missing or unreadable
前端·chrome
云飞云共享云桌面14 小时前
面向机械研发:双服务器架构搭配云飞云运行 SolidWorks 方案详解
运维·服务器·前端·网络·架构·制造
乐兮创想 小林15 小时前
B2B 内容营销的工程化运营:从内容矩阵建模到 SEO/GEO 联动的完整体系
前端·线性代数·矩阵·网站建设·北京网站建设公司
2501_9400417415 小时前
全栈开发提速指南:可以直接用的项目生成提示词
前端·prompt
BomanGe215 小时前
NSK直线导轨LH55EL与NH55EM替代指南
前端·javascript·数据库·经验分享·规格说明书
云水一下15 小时前
Vue.js从零到精通系列(四):前端路由与Vue Router——打造多页单页应用
前端·javascript·vue.js
糯米导航15 小时前
浏览器解析HTML头部的底层逻辑:从字节流到渲染树的关键一步
前端·html