一、题目描述
在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗 cost[i] 升汽油。
你从某一个加油站出发,初始油箱为空。
如果可以绕环路行驶一周,则返回出发加油站的编号,否则返回 -1。
题目保证如果存在解,则 解是唯一的。
二、思路分析(贪心算法)
首先考虑一个关键问题:
如果 所有加油站的总油量 < 总消耗油量,那么无论从哪里出发都不可能绕一圈。
因此:
sum(gas) >= sum(cost) 才可能有解
接下来思考如何找到起点。
我们定义:
diff[i] = gas[i] - cost[i]
表示在第 i 个加油站加油后前往下一个站的 净油量变化。
然后遍历数组,并维护两个变量:
-
total:整个环路的油量盈余 -
tank:当前起点到当前位置的剩余油量
遍历过程:
-
累加
diff -
如果
tank < 0,说明:-
当前起点无法到达
i+1 -
当前起点到 i 之间的任何站都不可能成为起点
-
因此可以直接把起点更新为:
start = i + 1
并把当前油量清零重新计算。
三、贪心策略为什么正确?
假设从 start 出发,到 i 时油量变为负数:
tank < 0
说明:
gas[start] + gas[start+1] + ... + gas[i]
<
cost[start] + cost[start+1] + ... + cost[i]
那么如果从 start+1、start+2 ... i 作为起点,情况只会更糟。
因此:
这些位置都不可能成为合法起点,可以直接跳过。
所以我们直接从 i+1 重新开始尝试。
四、算法步骤
-
遍历所有加油站
-
计算
diff = gas[i] - cost[i] -
更新
total和tank -
如果
tank < 0-
起点更新为
i+1 -
tank = 0
-
-
遍历结束后
-
如果
total >= 0返回start -
否则返回
-1
-
五、C语言代码实现
int canCompleteCircuit(int* gas, int gasSize, int* cost, int costSize) {
int total = 0; // 总油量差
int tank = 0; // 当前油量
int start = 0; // 起点
for (int i = 0; i < gasSize; i++) {
int diff = gas[i] - cost[i];
total += diff;
tank += diff;
// 当前起点无法继续前进
if (tank < 0) {
start = i + 1;
tank = 0;
}
}
if (total >= 0)
return start;
else
return -1;
}
六、复杂度分析
时间复杂度
O(n)
只遍历一次数组。
空间复杂度
O(1)
只使用常数级变量。
七、总结
本题核心有两个关键点:
1️⃣ 总油量必须 ≥ 总消耗油量
否则一定无解。
2️⃣ 贪心选择起点
一旦当前起点无法到达某个位置,则:
start ~ i 之间的所有点都不可能成为起点
因此直接从 i+1 重新开始。
该方法只需一次遍历即可解决问题,是本题的 最优解法。