盘点面试大热门之区间问题

关卡名

理解与贪心有关的高频问题

我会了✔️

内容

1.理解区间问题如何解决

✔️

2.理解字符串分割问题

✔️

3.理解加油站问题如何解决

✔️

1. 区间问题 

区间问题也是面试中经常遇到的情况,这类面试题目还挺讨巧的,很容易考察出应聘者到底会不会写代码。我们来看几个例子。两个区间所有可能的关系如下所示,对于所有区间的问题,我们都在大脑里装着这张图,然后再看题,会容易很多。

1.1 判断区间是否重叠

LeetCode252.给定一个会议时间安排的数组 intervals ,每个会议时间都会包括开始和结束的时间 intervals[i] = [starti, endi] ,请你判断一个人是否能够参加这里面的全部会议。

示例 1::

输入: intervals = [[0,30],[15,20],[5,10]]

解释: 存在重叠区间,一个人在同一时刻只能参加一个会议。

因为一个人在同一时刻只能参加一个会议,因此题目实质是判断是否存在重叠区间,将区间按照会议开始时间进行排序,然后遍历一遍判断后面的会议开始的时候是否前面的还没有结束即可。也就是上图中如果出现重叠的部分就直接返回false即可。

public boolean canAttendMeetings(int[][] intervals) {// 将区间按照会议开始实现升序排序Arrays.sort(intervals, (v1, v2) -> v1[0] - v2[0]);// 遍历会议,如果下一个会议在前一个会议结束之前就开始了,返回 false。for (int i = 1; i < intervals.length; i++) {if (intervals[i][0] < intervals[i - 1][1]) {return false;}}return true;
}

1.2 合并区间 

LeetCode56.以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。

示例1:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]

输出:[[1,6],[8,10],[15,18]]

解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

为了方便理解,我们将上述序列画成序列图:

和上一题一样,首先对区间按照起始端点进行升序排序,然后逐个判断当前区间是否与前一个区间重叠,如果不重叠的话将当前区间直接加入结果集,反之如果重叠的话,就将当前区间与前一个区间进行合并。

public int[][] merge(int[][] intervals) {// 先按照区间起始位置排序Arrays.sort(intervals, (v1, v2) -> v1[0] - v2[0]);// 遍历区间int[][] res = new int[intervals.length][2];int idx = -1;for (int[] interval: intervals) {// 如果结果数组是空的,或者当前区间的起始位置 > 结果数组中最后区间的终止位置,说明不重叠。// 则不合并,直接将当前区间加入结果数组。if (idx == -1 || interval[0] > res[idx][1]) {res[++idx] = interval;} else {// 反之说明重叠,则将当前区间合并至结果数组的最后区间res[idx][1] = Math.max(res[idx][1], interval[1]);}}return Arrays.copyOf(res, idx + 1);
}

1.3 插入区间 

LeetCode57,给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。

示例 1::

输入: intervals = [[1,3],[6,9]], newInterval = [2,5]

输出: [[1,5],[6,9]]

解释: 新区间[2,5] 与 [1,3]重叠,因此合并成为 [1,5]。

示例 2::

输入: intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8]

输出: [[1,2],[3,10],[12,16]]

解释: 新区间 [4,8] 与 [3,5],[6,7],[8,10] 重叠,因此合并成为 [3,10]。

本题就是上一题的再拓展,本题中的区间已经按照起始端点升序排列,因此我们直接遍历区间列表,寻找新区间的插入位置即可。具体步骤如下:

  1. 首先将新区间左边且相离的区间加入结果集(遍历时,如果当前区间的结束位置小于新区间的开始位置,说明当前区间在新区间的左边且相离);
  2. 接着判断当前区间是否与新区间重叠,重叠的话就进行合并,直到遍历到当前区间在新区间的右边且相离,将最终合并后的新区间加入结果集;
  3. 最后将新区间右边且相离的区间加入结果集。

public int[][] insert(int[][] intervals, int[] newInterval) {int[][] res = new int[intervals.length + 1][2];int idx = 0;// 遍历区间列表:// 首先将新区间左边且相离的区间加入结果集int i = 0;while (i < intervals.length && intervals[i][1] < newInterval[0]) {res[idx++] = intervals[i++];}// 判断当前区间是否与新区间重叠,重叠的话就进行合并,直到遍历到当前区间在新区间的右边且相离,// 将最终合并后的新区间加入结果集while (i < intervals.length && intervals[i][0] <= newInterval[1]) {newInterval[0] = Math.min(intervals[i][0], newInterval[0]);newInterval[1] = Math.max(intervals[i][1], newInterval[1]);i++;}res[idx++] = newInterval;// 最后将新区间右边且相离的区间加入结果集while (i < intervals.length) {res[idx++] = intervals[i++];}return Arrays.copyOf(res, idx);
}

2 字符串分割

这个题第一次做会感觉特别难, 无从下手,或者想的很复杂,但简单的方法就能解决。
LeetCode763 . 字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。

输入:S = "ababcbacadefegdehijhklij"

输出:[9,7,8]

解释:

划分结果为 "ababcbaca", "defegde", "hijhklij"。

每个字母最多出现在一个片段中。

像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。

我们在回溯部分会讲到,分割问题是回溯算法的典型应用,但是这个题如果用回溯将非常复杂。题目要求同一字母最多出现在一个片段中,那么如何把同一个字母的都圈在同一个区间里呢?
该遍历过程相当要找每一个字母的边界,如果找到之前遍历过的所有字母的最远边界,说明这个边界就是分割点了。此时前面出现过所有字母,最远也就到这个边界了。所以可以这么做:

  • 首先,统计每一个字符最后出现的位置
  • 然后,从头遍历字符,并更新字符的最远出现下标,如果找到字符最远出现位置下标和当前下标相等了,则找到了分割点。 

 

public List<Integer> partitionLabels(String S) {List<Integer> list = new LinkedList<>();int[] edge = new int[26];char[] chars = S.toCharArray();for (int i = 0; i < chars.length; i++) {edge[chars[i] - 'a'] = i;}int idx = 0;int last = -1;for (int i = 0; i < chars.length; i++) {idx = Math.max(idx,edge[chars[i] - 'a']);if (i == idx) {list.add(i - last);last = i;}}return list;
}

3 加油站问题

加油站问题也是贪心的热门问题之一。LeetCode134.题目要求:在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。

示例1

输入:

gas = [1,2,3,4,5]

cost = [3,4,5,1,2]

输出: 3

解释:

从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油

开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油

开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油

开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油

开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油

开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。

因此,3 可为起始索引。

我们说做贪心题目时,不用管啥是贪心,直接做题就行。我们先理解一下这个题是什么意思。示例给的两个数组只是代表从当前站可以提供多少油料,以及从前一个位置过来需要消耗多少油料,因为是环。所以这里gas[0]=1和cost[0]=3就表示第一个站有1升汽油,从第一个站到第二个站需要消耗3升汽油。
很显然在第一个站只能加1升汽油,但是从第一到第二个站需要消耗3升汽油,因此不能从第一个站开始走。为此我们可以从第二个,第三个、第四个开始,因此我们想到了暴力解法,如下图所示:

 

我们一直向后找,只要能找到就行。
这里很明显的问题就是需要大量的重复计算,我们来优化一下。
首先,如果总油量减去总消耗大于等于零,那么应该能跑完一圈,具体到每一段就是各个加油站的剩油量rest[i]相加一定是大于等于零的。
每个加油站的剩余量rest[i]为gas[i] - cost[i]。i从0开始累加rest[i],和记为curSum,一旦curSum小于零,说明[0, i]区间都不能作为起始位置,起始位置必须从i+1开始重新算,只有这样才能保证我们有可能完成。

这个操作能让搜索从O(n^2)降低到O(n),代码如下:

public int canCompleteCircuit(int[] gas, int[] cost) {int curSum = 0;int totalSum = 0;int start = 0;for (int i = 0; i < gas.length; i++) {curSum += gas[i] - cost[i];totalSum += gas[i] - cost[i];// 当前累加rest[i]和 curSum一旦小于0if (curSum < 0) {  // 更新起始位置为i+1 start = i + 1; // curSum从0开始 curSum = 0;     }}// 说明怎么走都不可能跑一圈了if (totalSum < 0) return -1; return start;
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/265553.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙应用开发(二)环境搭建

开发流程 IDE下载 首先下载HUAWEI DevEco Studio&#xff0c;介绍首次启动DevEco Studio的配置向导&#xff1a; 运行已安装的DevEco Studio&#xff0c;首次使用&#xff0c;请选择Do not import settings&#xff0c;单击OK。安装Node.js与ohpm。node.js 是基于 V8 引擎构…

基于lambda简化设计模式

前言 虽说使用设计模式可以让复杂的业务代码变得清晰且易于维护&#xff0c;但是某些情况下&#xff0c;开发可能会遇到我为了简单的业务逻辑去适配设计模式的情况&#xff0c;本文笔者就以四种常见的设计模式为例&#xff0c;演示如何基于lambda来简化设计模式的实现。 策略…

玩转大数据14:分布式计算框架的选择与比较

1. 引言 随着大数据时代的到来&#xff0c;越来越多的企业和组织需要处理海量数据。分布式计算框架提供了一种有效的方式来解决大数据处理的问题。分布式计算框架将计算任务分解成多个子任务&#xff0c;并在多个节点上并行执行&#xff0c;从而提高计算效率。 2. 分布式计算…

你的轻量化设计能有效提高模型的推理速度吗?

写在前面&#xff1a;本博客仅作记录学习之用&#xff0c;部分图片来自网络&#xff0c;如需引用请注明出处&#xff0c;同时如有侵犯您的权益&#xff0c;请联系删除&#xff01; 文章目录 前言预备知识模型指标MACs计算卷积MACs全连接MACs激活函数MACsBN MACs 存储访问存储构…

基于SpringBoot+Vue前后端分离的景点数据分析平台(Java毕业设计)

大家好&#xff0c;我是DeBug&#xff0c;很高兴你能来阅读&#xff01;作为一名热爱编程的程序员&#xff0c;我希望通过这些教学笔记与大家分享我的编程经验和知识。在这里&#xff0c;我将会结合实际项目经验&#xff0c;分享编程技巧、最佳实践以及解决问题的方法。无论你是…

彻底搞懂Python常用时间库time、datetime与时间格式之间的转换关系

Python项目中很多时候会需要将时间在Datetime格式和TimeStamp格式之间转化&#xff0c;本文将对python中多个时间储存方式、时间模块(如time、datetime、timeit)以及他们之间的转换关系进行详细的梳理和总结。整体梳理后表示图如下&#xff1a; 一&#xff0c;python中储存时间…

Ubuntu下安装SDL

源码下载地址&#xff08;SDL version 2.0.14&#xff09;&#xff1a;https://www.libsdl.org/release/SDL2-2.0.14.tar.gz 将源码包拷贝到系统里 使用命令解压 tar -zxvf SDL2-2.0.14.tar.gz 解压得到文件夹 SDL2-2.0.14 进入文件夹 执行命令 ./configure 执行命令 make…

静态SOCKS5:了解基本概念和协议

SOCKS5是一种网络协议&#xff0c;是SOCKS协议的第五个版本&#xff0c;它提供了一种安全的、加密的网络连接&#xff0c;可以帮助用户在互联网上保护自己的隐私和安全。静态SOCKS5是指使用静态IP地址和端口的SOCKS5代理服务器&#xff0c;这种代理服务器可以提供更稳定、更快速…

解决vue3 动态引入报错问题

之前这样写的&#xff0c;能使用&#xff0c;但是有警告 警告&#xff0c;查了下&#xff0c;是动态引入的问题&#xff0c;看到说要用glob 然后再我的基础上&#xff0c;稍微 改了下&#xff0c;就可以了&#xff1a; 最后打印了下&#xff0c;modules[../../components/flowc…

SPI 通信-stm32入门

本节我们将继续学习下一个通信协议 SPI&#xff0c;SPI 通信和我们刚学完的 I2C 通信差不多。两个协议的设计目的都一样&#xff0c;都是实现主控芯片和各种外挂芯片之间的数据交流&#xff0c;有了数据交流的能力&#xff0c;我们主控芯片就可以挂载并操纵各式各样的外部芯片&…

如何使用cpolar+Inis在Ubuntu系统快速搭建本地博客网站公网可访问

文章目录 前言1. Inis博客网站搭建1.1. Inis博客网站下载和安装1.2 Inis博客网站测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar临时数据隧道2.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;2.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 3. 公网访问测试总…

【数学建模】《实战数学建模:例题与讲解》第九讲-时间序列分析(含Matlab代码)

【数学建模】《实战数学建模&#xff1a;例题与讲解》第九讲-时间序列分析&#xff08;含Matlab代码&#xff09; 基本概念确定性时间序列分析方法平稳时间序列模型ARIMA模型季节性序列 习题8.11. 题目要求2.解题过程3.程序4.结果 习题8.21. 题目要求2.解题过程3.程序4.结果 习…