题目描述
在一条环形路上有 n 个加油站,每个加油站 i 有 gas[i] 升汽油。汽车从第 i 站到第 i+1 站需要消耗 cost[i] 升汽油。假设油箱容量无限,但初始为空,请判断是否存在一个起点使得汽车能绕环路行驶一周。若存在,返回该起点的索引,否则返回 -1。
示例 1:
输入: gas = [1,2,3,4,5], cost = [3,4,5,1,2]
输出: 3
解释:
从 3 号加油站出发:
- 获得 4 升油,消耗 1 升到达 4 号站(剩余 3 升)。
- 获得 5 升油,消耗 2 升到达 0 号站(剩余 6 升)。
- 获得 1 升油,消耗 3 升到达 1 号站(剩余 4 升)。
- 获得 2 升油,消耗 4 升到达 2 号站(剩余 2 升)。
- 获得 3 升油,消耗 5 升返回 3 号站(剩余 0 升),恰好完成一圈。
示例 2:
输入: gas = [2,3,4], cost = [3,4,3]
输出: -1
解释:
无论从哪个站点出发,油量均不足以绕行一周。
算法思路
贪心策略:通过一次遍历确定唯一可能的起点,结合总油量与总消耗的关系快速判断可行性。
核心思想
- 总油量检查:若总油量
sum(gas) < sum(cost),直接返回-1。 - 局部油量追踪:维护当前油量
currentGas,若在某点油量为负,则起点必不在此点之前,重置起点为下一站点。
具体步骤
- 初始化变量:
totalGas和totalCost分别记录总油量和总消耗。currentGas记录当前剩余油量。startIdx记录可能的起点。
- 遍历所有站点:
- 累加
totalGas和totalCost。 - 更新
currentGas为当前油量差(gas[i] - cost[i])。 - 若
currentGas < 0,重置起点为i+1,并清空当前油量。
- 累加
- 最终判断:
- 若总油量不足,返回
-1。 - 否则返回
startIdx。
- 若总油量不足,返回
复杂度分析
- 时间复杂度:O(n),仅需一次遍历。
- 空间复杂度:O(1),仅使用常量空间。
代码实现
func canCompleteCircuit(gas []int, cost []int) int {
n := len(gas)
startIdx := 0 // 可能的起点索引
totalGas := 0 // 总油量
totalCost := 0 // 总消耗
currentGas := 0 // 当前剩余油量
for i := 0; i < n; i++ {
totalGas += gas[i]
totalCost += cost[i]
currentGas += gas[i] - cost[i]
// 若当前油量为负,重置起点并清空油量
if currentGas < 0 {
startIdx = i + 1
currentGas = 0
}
}
// 总油量不足,无法绕行
if totalGas < totalCost {
return -1
}
return startIdx
}
关键点总结
- 总油量检查:若总油量不足,直接排除可能。
- 贪心策略:通过局部油量判断起点位置,避免无效遍历。
- 唯一解保证:题目保证若有解则唯一,算法正确性基于此特性。
示例解析
示例1解析:
- 总油量:1+2+3+4+5 = 15,总消耗:3+4+5+1+2 = 15,油量足够。
- 遍历过程:
- i=0:
currentGas = -2→ 重置起点为1,油量清零。 - i=1:
currentGas = -2→ 重置起点为2,油量清零。 - i=2:
currentGas = -2→ 重置起点为3,油量清零。 - i=3:
currentGas = 3→ 保持起点3。 - i=4:
currentGas = 6→ 保持起点3。
- i=0:
- 最终返回:3。
示例2解析:
- 总油量:2+3+4 = 9,总消耗:3+4+3 = 10,总油量不足,直接返回
-1。