1 134. 加油站
题目:在一条环路上有 n
个加油站,其中第 i
个加油站有汽油 gas[i]
升。你有一辆油箱容量无限的的汽车,从第 i
个加油站开往第 i+1
个加油站需要消耗汽油 cost[i]
升。你从其中的一个加油站出发,开始时油箱为空。给定两个整数数组 gas
和 cost
,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1
。如果存在解,则 保证 它是 唯一 的。
提示:
gas.length == n
cost.length == n
1 <= n <= 105
0 <= gas[i], cost[i] <= 104
思路:本题基础解题思路为暴力遍历,从第一个加油站开始,依次向后遍历判断当前油量总和sum=sum+gas[i]是否能大于当前油站消耗cost[i],如果小于则向后遍历直到遍历结束。因为题目设置为环形路线,此处使用取余操作模拟环路,当遍历一圈或找到符合要求的加油站时结束。 在力扣上暴力遍历超出时间限制,故此不推荐。
贪心算法局部最优解,根据每个油站的剩余油量rest[i]=gas[i]-cost[i]的累加结果来判断。在[0,i]的区间内,当curSum出现负数情况,即表示该区间内的节点不能满足供油需求,重新开始判断需从i+1号油站开始,当所有油站都计算完毕,根据totalSum结果判断当前开始油站是否满意要求。
注意:暴力遍历时为模拟环形数组使用取余操作和while循环配合。
Java实现
class Solution {
// 暴力遍历,时间复杂度O(n^2),超出时间限制
// public int canCompleteCircuit(int[] gas, int[] cost) {
// for(int i=0;i<gas.length;i++){
// int rest = gas[i]-cost[i];
// int start = (i+1)%gas.length;
// while(rest>0 && start!=i){
// rest += gas[start] - cost[start];
// start = (start+1)%gas.length;
// }
// if(rest>=0 && start==i){
// return i;
// }
// }
// return -1;
// }
// 局部最优,区间内[0,i]判断是否满足油量,不满足从i+1开始重新计算
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];
if(curSum<0){
start = i+1;
curSum = 0;
}
}
if(totalSum<0){
return -1;
}
return start;
}
}
2 135. 分发糖果
题目:n
个孩子站成一排。给你一个整数数组 ratings
表示每个孩子的评分。你需要按照以下要求,给这些孩子分发糖果:
- 每个孩子至少分配到
1
个糖果。 - 相邻两个孩子评分更高的孩子会获得更多的糖果。
请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。
提示:
n == ratings.length
1 <= n <= 2 * 104
0 <= ratings[i] <= 2 * 104
思路: 借助卡尔老师的一句话,“贪心算法,两者兼顾容易顾此失彼!”。
本题分为确保“右边孩子比我大的比我多一”和“左边孩子比我大的比我多一”两部分。首先为第一个孩子分1个糖果,因为每个孩子至少有一个。为保证“左边孩子比我大的比我多一”,从左向右依次遍历,当遇到大的在当前孩子基础上+1,相同或者小的赋值为1。同理“右边孩子比我大的比我多一”为从右向左遍历。
注意:两个方向上分别遍历时,遍历区间要分清楚。从左向右为[1,ratings.length),从右向左为[0,ratings.length-2]。糖果赋值时可以使用 变量=(条件)?运算1:运算2 的操作。
Java实现
class Solution {
public int candy(int[] ratings) {
int counts = ratings.length;
int[] count = new int[counts];
count[0] = 1;
for(int i=1; i<counts; i++){
count[i] = (ratings[i]>ratings[i-1])?count[i-1]+1:1;
}
for(int i=counts-2; i>=0; i--){
if(ratings[i]>ratings[i+1]){
count[i] = (count[i]<count[i+1]+1)?count[i+1]+1:count[i];
}
}
int sum = 0;
for(int num : count){
sum += num;
}
return sum;
}
}
3 860. 柠檬水找零
题目:在柠檬水摊上,每一杯柠檬水的售价为 5
美元。顾客排队购买你的产品,(按账单 bills
支付的顺序)一次购买一杯。每位顾客只买一杯柠檬水,然后向你付 5
美元、10
美元或 20
美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5
美元。
注意,一开始你手头没有任何零钱。给你一个整数数组 bills
,其中 bills[i]
是第 i
位顾客付的账。如果你能给每位顾客正确找零,返回 true
,否则返回 false
。
提示:
1 <= bills.length <= 105
bills[i]
不是5
就是10
或是20
思路:本题初步思路为从前向后遍历,根据找零结果修改数组元素。当元素为五元时不做操作;元素为十元或二十元时,计算找零数值,在当前位置向前遍历减去数组值并保证找零值不为负。该思路弊端在于,当找零为十五元时,不能优先用十元,从而导致五元出现短缺。
再思考本题,数组中只有5,10,20三种情况。当5时,不需要找零操作,count5+1即可;当10时,需找零五元,即count5-1,count10+1;当20时,优先选择count10-1和count5-1的组合,若没有10,则选择count5-3的组合,因此当遍历中count5或count10出现负数,则证明找零失败。
注意:操作有限情况下优先考虑一一对应的解题思路。
Java实现
class Solution {
// 55/61测试用例通过
// public boolean lemonadeChange(int[] bills) {
// for(int i=0; i<bills.length; i++){
// if(bills[i]>5){
// int sign = i;
// int cash = bills[i] - 5;
// while(sign>0){
// if(cash-bills[sign-1]>=0){
// cash -= bills[sign-1];
// bills[sign-1] = 0;
// sign--;
// }
// else{
// sign--;
// }
// }
// if(cash>0){
// return false;
// }
// }
// }
// return true;
// }
// 找零的情况只有三种,五元直接找零,十元用五元找零,二十元尽量用十元+五元找零
public boolean lemonadeChange(int[] bills) {
int five = 0;
int ten = 0;
for(int i=0; i<bills.length; i++){
if(bills[i]==5){
five++;
}else if(bills[i]==10){
five--;
ten++;
}else if(bills[i]==20){
if(ten>0){
ten--;
five--;
}
else{
five -= 3;
}
}
if(five<0 || ten<0){
return false;
}
}
return true;
}
}
4 406. 根据身高重建队列
题目:假设有打乱顺序的一群人站成一个队列,数组 people
表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki]
表示第 i
个人的身高为 hi
,前面 正好 有 ki
个身高大于或等于 hi
的人。请你重新构造并返回输入数组 people
所表示的队列。返回的队列应该格式化为数组 queue
,其中 queue[j] = [hj, kj]
是队列中第 j
个人的属性(queue[0]
是排在队列前面的人)
提示:
1 <= people.length <= 2000
0 <= hi <= 106
0 <= ki < people.length
- 题目数据确保队列可以被重建
思路:解答本题与 135.分发糖果有相似的解题思路,贪心算法分别分两种条件逐一考虑。先根据身高从大到小排序,再根据k值逐一插入。但是很可惜答主本人一开始甚至没读懂题意,根据给出的示例结果也没理解,这题看了讲解才懂的。把讲解贴出来,朋友们自行学习一下 406.根据身高重建队列
注意:存在二维数组,Java中数据结构的选择
Java实现
class Solution {
public int[][] reconstructQueue(int[][] people) {
// 身高排序
Arrays.sort(people,(a,b)->{
if(a[0]==b[0])
return a[1]-b[1];
return b[0]-a[0];
});
LinkedList<int[]> que = new LinkedList<>();
for(int[] p : people){
que.add(p[1],p);
}
return que.toArray(new int[people.length][]);
}
}