134. 加油站
✨核心逻辑
本题使用 贪心算法 的思想,只需要一次遍历就能找到唯一的出发点:
- 全局判断:如果所有加油站的总油量(
totalGas)小于总耗油量(totalCost),即totalGas < 0,说明无论从哪个站点出发都无法绕行一周,直接返回-1。 - 局部贪心:用一个变量
currentGas记录从当前起点出发,到达当前站点时的油箱剩余油量。 - 重置起点:如果在行驶到站点
i时,发现currentGas < 0,说明从当前记录的起点到站点i这段区间内的任何一点出发,都无法跨越i这关。因此,直接抛弃这段区间,将下一个站点i + 1设为新的起点,并将currentGas重置为0。 - 最终答案:当遍历结束时,如果总油量是充足的,记录下的
start索引就是唯一可行的出发点。
🔥代码实现(含详细变量注释)
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
// totalGas:记录所有加油站的油量总和减去所有路段的油耗总和
// 如果最终 totalGas < 0,说明总油量不够,必然无法跑完一圈
int totalGas = 0;
// currentGas:记录从当前起点 start 出发,目前油箱里剩下的油量
int currentGas = 0;
// start:记录当前判断出的可行的起始加油站索引(初始默认为 0)
int start = 0;
// i:遍历指针,依次走过每一个加油站
for (int i = 0; i < cost.length; i++) {
// diff:计算当前站点获得的油量 gas[i] 与前往下一站需要消耗的油量 cost[i] 的差值
int diff = gas[i] - cost[i];
// 累加差值到总油量和当前剩余油量中
totalGas += diff;
currentGas += diff;
// 核心判断:如果到达当前站点 i 时,油箱里油量出现负数
// 说明从当前的 start 到 i 之间的任意一点出发,都无法顺利通过 i 站
// 因此将 i + 1 作为新的潜在出发点继续尝试
if (currentGas < 0) {
start = i + 1;
currentGas = 0; // 重新从新起点出发,油箱清空,从 0 开始累加
}
}
// 如果所有站点的净油量总和小于 0,则无解返回 -1;否则返回记录的有效起点 start
return totalGas < 0 ? -1 : start;
}
}
- ⏱️复杂度分析
时间复杂度:O(N),其中 N 是数组的长度。只需遍历数组一次,在循环内部进行常数次的加减和比较操作。
空间复杂度:O(1),仅仅使用了 totalGas、currentGas、start、diff 和循环变量 i 等常数个整型变量,没有使用额外的数组空间。
🔍总结一下:
这道题采用贪心,看完答案还是有一个疑惑点: A-B 的路途中假设在B处油不够,为什么直接让起始start变成 B+1 而不是从之前A+1位置处重新走呢??
因为A能到B,说明中间的油一定有积累,从 A+1 开始少了A这部分余留的油一定到不了B!!
所以这道题就“贪”在这里