力扣面试150(15/150)
7.4 134. 加油站
在一条环路上有 n
个加油站,其中第 i
个加油站有汽油 gas[i]
升。
你有一辆油箱容量无限的的汽车,从第 i
个加油站开往第 i+1
个加油站需要消耗汽油 cost[i]
升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组 gas
和 cost
,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1
。如果存在解,则 保证 它是 唯一 的。
我的思路:我最开始没有思路,只有想到循环,我也没有实现
看了一下题解,说是贪心算法,使用总gas-总cost
贪心:总gas-总消耗>0=>代表它是可以的
局部最优:在进行循环遍历的时候,如果curCost<0,那么就证明当前并不是最优的路径,我们就i++,curCost=0
全局最优的保证:最终检查 total(总汽油量是否非负),如果 total >= 0,说明一定存在一个解,而之前选择的 ans 就是全局最优的起点
我的代码:
function canCompleteCircuit(gas: number[], cost: number[]): number {let total = 0 //总的汽油=gas-costlet curCost = 0//当前的消耗let ans = 0;for(let i = 0 ; i < gas.length ; i++){curCost += gas[i] - cost[i];total += gas[i] - cost[i];if(curCost < 0){ans = i + 1;curCost = 0;}}if(total < 0) return -1;return ans;};
法2:
最优解一定是在最大消耗量的后一个(总油量-总消耗>0的情况下)
我的代码:
let maxCost = 0;let curCost = 0;let ans = 0 ;for(let i = 0 ; i < gas.length ; i++){curCost += gas[i] - cost[i];if(curCost < maxCost){maxCost = curCost;ans = i+1;}}if(curCost < 0){return -1;}return ans;
总结:
加油站问题可以用贪心算法解决:
- 核心思路:从某个起点出发,如果行驶过程中油量不足(curCost < 0),则该起点无效,直接跳到下一个加油站作为新起点,并重置当前油量。
- 全局判断:如果所有加油站的总油量 >= 总消耗量,则一定存在解,且贪心算法找到的起点就是唯一解;否则返回 -1。
- 效率:只需一次遍历,时间复杂度 O(n),非常高效。
7.4 135. 分发糖果
n
个孩子站成一排。给你一个整数数组 ratings
表示每个孩子的评分。
你需要按照以下要求,给这些孩子分发糖果:
- 每个孩子至少分配到
1
个糖果。 - 相邻两个孩子评分更高的孩子会获得更多的糖果。
请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。
我的思路:
先保证每人一个
candy=ratings.length
然后两个两个进行比较
不行不行
新建一个数组来装糖果的数量
两个两个考虑没有考虑全局
我的代码:
let n = ratings.length;let ans = n;let candy = new Array(n).fill(1);for (let i = 0; i < n - 1; i++) {if (ratings[i] > ratings[i + 1]) {if (candy[i] <= candy[i + 1]) {let temp = candy[i];candy[i] += candy[i + 1] - temp + 1;ans += candy[i + 1] - temp + 1;}} else if (ratings[i] < ratings[i + 1]) {if (candy[i + 1] <= candy[i]) {let temp = candy[i+1];candy[i + 1] += candy[i] - temp + 1;ans += candy[i] - temp + 1;}}}// 再次遍历看是否有纰漏for (let i = 0; i < n-1; i++) {if (ratings[i] > ratings[i + 1] && candy[i] <= candy[i + 1]) {let temp = candy[i];candy[i] += candy[i + 1] - temp + 1;ans += candy[i + 1] - temp + 1;}else if(ratings[i] < ratings[i+1]&& candy[i+1] <= candy[i]){let temp = candy[i+1];candy[i + 1] +=candy[i] - temp + 1;ans += candy[i] - temp + 1;}}return ans;
报错报错!满足了这个错又有那个错!
题解:
- 从左到右遍历:
- 遍历所有孩子,从第二个孩子开始,如果当前孩子的评分比左边孩子高,则当前孩子的糖果数比左边孩子多一颗。
- 从右到左遍历:
- 遍历所有孩子,从倒数第二个孩子开始,如果当前孩子的评分比右边孩子高,则当前孩子的糖果数取当前糖果数和右边孩子糖果数加一中的较大值。
代码:
function candy(ratings: number[]): number {let n = ratings.length;let candy = new Array(n).fill(1);let ans = n; // 初始每个孩子至少一颗糖果// 从左到右遍历,确保比左边孩子评分高的孩子比左边孩子多一颗糖果for (let i = 1; i < n; i++) {if (ratings[i] > ratings[i - 1]) {candy[i] = candy[i - 1] + 1;}}// 从右到左遍历,确保比右边孩子评分高的孩子比右边孩子多一颗糖果for (let i = n - 2; i >= 0; i--) {if (ratings[i] > ratings[i + 1]) {candy[i] = Math.max(candy[i], candy[i + 1] + 1);}}// 计算总糖果数ans = candy.reduce((sum, num) => sum + num, 0);
总结:
通过两次遍历,我们可以确保每个孩子的糖果数满足评分高的孩子比相邻孩子多糖果的要求。最终计算所有孩子分到的糖果数的总和,就是问题的答案。