提示:如果生活把你的门关上了,那你就再打开,这就是门,门就是这样的。 --佚名
贪婪的思想不一定要理解的很透彻,但是贪婪的问题我们要掌握的很好,这里我们就研究一些高频的考察题目。
区间问题就是典型的面试中常见到的,这类面试题还是很受欢迎的,容易考察应聘者到底会不会写好代码。我们看看一些区间的例子.两个区间的所有可能关系如下所示:
对于所有区间的问题,我们还是看看下面的图,一定要记住这些关系,很常见,也很容易出。
参考地址连接:【leetcode】252 会议室(数组)_给定一个会议时间安排的数组-CSDN博客
看下图:
因为一个人同一时刻只能参加一个会议,因此题目实质是判断是否存在重叠区间的。将区间按照会议开始时间进行排序,然后遍历一遍判断后面的会议开始的时候是否前面的会议还没有结束即可,也就是上面的图中所显示的,如果存在重叠直接返回false即可。
/**
* 会议室(数组)
*
* @param intervals
* @return
*/
public static boolean canAttendMeetings(int[][] intervals) {
//将区间排序
Arrays.sort(intervals, (v1, v2) -> v1[0] - v2[0]);
// 遍历所有会议,是否出现重复 ( 技巧 从1 开始
for (int i = 1; i < intervals.length; i++) {
if (intervals[i][0] < intervals[i - 1][1]) {
return false;
}
}
return true;
}
参考题目参数:56. 合并区间 - 力扣(LeetCode)
看下图:
和上一题一样,首先对区间按照起点端点进行升序排序,然后逐个判断当前区间是否与前一个区间重叠,如果不重叠的话直接加入结果集,反之重叠的话(当前区间与前一个区间进行合并)
/**
* 合并区间
* @param intervals
* @return
*/
public static int[][] merge(int[][] intervals) {
// 排序数组
Arrays.sort(intervals,(v1,v2) ->v1[0] - v2[0]);
// 创建新的区间
int[][] merge = new int[intervals.length][2];
int idx = -1;
// 遍历数组
for(int [] interval: intervals){
// 如果数组是空的或者当前区间的起始位置 > 结果数组中最后区间的最大值
// 不合并区间,直接加入区间
if (idx == -1 && interval[0] > merge[idx][1]){
merge[++idx] = interval;
}else{
// 反之说明重叠,则将当前区间合并至结果数组的最后区间
merge[idx][1] = Math.max(merge[idx][1],interval[1]);
}
}
return Arrays.copyOf(merge, idx + 1);
}
参考题目地址:57. 插入区间 - 力扣(LeetCode)
本题目也是上一个题目的扩展,这里区间已经按照起始结束升序排列了,我们可以直接遍历区间,寻找新区间的插入位置就可以了。
/**
* 插入区间
* @param intervals
* @param newInterval
* @return
*/
public static int[][] insert(int[][] intervals, int[] newInterval) {
// 创建一个足够大的数组
int[][] res = new int[intervals.length + 1][2];
int idx = 0;
// 左边的先进入(intervals[i][1] < newInterval[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(newInterval[0], intervals[i][0]);
// 最右端
newInterval[1] = Math.min(newInterval[1],intervals[i][0]);
i++;
}
// 将合并的区间放入结果集
res[idx++]= newInterval;
// 放入右边的,直接放入结果集
while(i < intervals.length){
res[idx++]= intervals[i++];
}
return Arrays.copyOf(res,idx);
}
参考题目地址:763. Partition Labels - 力扣(LeetCode)
这个问题,有点回溯。分割问题是回溯算法的典型应用,但是这个问题如果用回溯将很复杂。题目要求同一个字母最多出现在一个片段中,那么要如何进行分片才算合理呢?
该遍历过程相当于找每个字母的边界,如果知道之前遍历过的所有字母的最远边界,说明这个边界就是分割点了,此时前面出现的所有字母,最远也就到这个边界,所以做如下操作:
直接上代码:这个太神了
/**
* 划分字母区间
*
* @param S
* @return
*/
public static List<Integer> partitionLabels(String S) {
List<Integer> res = new ArrayList<Integer>();
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) {
// 这个非常巧妙
res.add(i - last);
last = i;
}
}
return res;
}
参考题目介绍:134. 加油站 - 力扣(LeetCode)
我们说做贪婪的题目,如果不管什么是贪婪,就直接做题。这里解释以下题目的含义:
实例给我们了两个数组,知识代表了当前站提供的油料,以及从前一个站过来需要消耗多少油料,因为是环,所以这里gas[0] = 1 和gas[0] = 3 就表示第一个站有1升汽油,从第一个站到第二个站需要消耗3升汽油.
当然最简单的方式是暴力从第一个试到最后一个。如图:
但是,这个问题需要大量的重复计算,优化以下:
public int canCompleteCircuit(int[] gas, int[] cost) {
int start = 0;
int totalSum = 0;
int curSum = 0;
for(int i = 0; i < gas.length; i++){
curSum += gas[i] - cost[i];
totalSum += gas[i] - cost[i];
if(curSum < 0){
// 更新起始位置
start = i + 1;
// 更新curSum
curSum = 0;
}
}
// 说明不能走一圈
if(totalSum < 0){
return -1;
}
return start;
}
提示:区间问题;字符串分割;加油站;贪婪算法;区间划分
如果有帮助到你,请给题解点个赞和收藏,让更多的人看到 ~ ("▔□▔)/
如有不理解的地方,欢迎你在评论区给我留言,我都会逐一回复 ~