leetcode 面试经典 150 题(14/150) 134. 加油站

65 阅读3分钟

题目描述

在一条环形路上有 n 个加油站,每个加油站 igas[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,若在某点油量为负,则起点必不在此点之前,重置起点为下一站点。

具体步骤

  1. 初始化变量
    • totalGastotalCost 分别记录总油量和总消耗。
    • currentGas 记录当前剩余油量。
    • startIdx 记录可能的起点。
  2. 遍历所有站点
    • 累加 totalGastotalCost
    • 更新 currentGas 为当前油量差(gas[i] - cost[i])。
    • currentGas < 0,重置起点为 i+1,并清空当前油量。
  3. 最终判断
    • 若总油量不足,返回 -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. 总油量检查:若总油量不足,直接排除可能。
  2. 贪心策略:通过局部油量判断起点位置,避免无效遍历。
  3. 唯一解保证:题目保证若有解则唯一,算法正确性基于此特性。

示例解析

示例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。
  • 最终返回:3。

示例2解析:

  • 总油量:2+3+4 = 9,总消耗:3+4+3 = 10,总油量不足,直接返回 -1