LeetCode第134题_加油站

LeetCode 第134题:加油站

题目描述

在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

给定两个整数数组 gascost ,如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。

难度

中等

题目链接

点击在LeetCode中查看题目

示例

示例 1:

输入: gas = [1,2,3,4,5], cost = [3,4,5,1,2]
输出: 3
解释:
从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油
开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油
开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油
开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油
开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油
开往 3 号加油站,此时油箱有 5 - 5 + 4 = 4 升汽油
你已经走完了一圈,返回 3

示例 2:

输入: gas = [2,3,4], cost = [3,4,3]
输出: -1
解释:
你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。
我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油
开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油
开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油
你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。

提示

  • gas.length == n
  • cost.length == n
  • 1 <= n <= 10^5
  • 0 <= gas[i], cost[i] <= 10^4

解题思路

方法一:暴力法

最直观的方法是尝试从每个加油站出发,模拟一次环路行驶,检查是否能够回到起点。

关键点:

  • 从每个加油站出发,模拟一次环路行驶
  • 如果在某个点油量不足,则该起点不可行
  • 如果能够回到起点,则找到了解

具体步骤:

  1. 遍历每个加油站作为起点
  2. 从起点开始,模拟环路行驶一周
  3. 如果在某个点油量不足,则该起点不可行,尝试下一个起点
  4. 如果能够回到起点,则返回起点索引
  5. 如果所有起点都不可行,则返回-1

时间复杂度:O(n²),其中n是加油站的数量。需要尝试n个起点,每次模拟需要O(n)的时间。
空间复杂度:O(1),只需要常数级别的额外空间。

方法二:一次遍历

我们可以通过一些数学推导,得到一个更高效的解法。

关键点:

  • 如果总油量小于总消耗量,则无解
  • 如果总油量大于等于总消耗量,则一定有解
  • 如果从起点i出发,到达j时油量不足,则i到j之间的任何一点都不可能作为起点

具体步骤:

  1. 计算总油量和总消耗量,如果总油量小于总消耗量,则返回-1
  2. 初始化起点start = 0,当前油量curGas = 0
  3. 遍历所有加油站:
    • 更新当前油量:curGas += gas[i] - cost[i]
    • 如果当前油量小于0,则将起点更新为下一个加油站,并将当前油量重置为0
  4. 返回起点start

时间复杂度:O(n),其中n是加油站的数量。只需要遍历一次加油站。
空间复杂度:O(1),只需要常数级别的额外空间。

图解思路

暴力法分析表

以示例1为例:gas = [1,2,3,4,5], cost = [3,4,5,1,2]

起点模拟过程结果
0起点油量: 0 + 1 = 1
到达1: 1 - 3 = -2 (油量不足)
不可行
1起点油量: 0 + 2 = 2
到达2: 2 - 4 = -2 (油量不足)
不可行
2起点油量: 0 + 3 = 3
到达3: 3 - 5 = -2 (油量不足)
不可行
3起点油量: 0 + 4 = 4
到达4: 4 - 1 = 3, 加油: 3 + 5 = 8
到达0: 8 - 2 = 6, 加油: 6 + 1 = 7
到达1: 7 - 3 = 4, 加油: 4 + 2 = 6
到达2: 6 - 4 = 2, 加油: 2 + 3 = 5
回到3: 5 - 5 = 0 (成功回到起点)
可行
4起点油量: 0 + 5 = 5
到达0: 5 - 2 = 3, 加油: 3 + 1 = 4
到达1: 4 - 3 = 1, 加油: 1 + 2 = 3
到达2: 3 - 4 = -1 (油量不足)
不可行

一次遍历分析表

以示例1为例:gas = [1,2,3,4,5], cost = [3,4,5,1,2]

索引gas[i]cost[i]gas[i] - cost[i]当前油量起点说明
初始状态---00初始起点为0
013-2-21油量不足,更新起点为1
124-2-22油量不足,更新起点为2
235-2-23油量不足,更新起点为3
341333油量充足,保持起点不变
452363油量充足,保持起点不变

总油量 = 1 + 2 + 3 + 4 + 5 = 15
总消耗量 = 3 + 4 + 5 + 1 + 2 = 15
总油量 >= 总消耗量,有解,起点为3

代码实现

C# 实现

public class Solution {
    public int CanCompleteCircuit(int[] gas, int[] cost) {
        int n = gas.Length;
        int totalGas = 0;
        int totalCost = 0;
        int curGas = 0;
        int start = 0;
        
        for (int i = 0; i < n; i++) {
            totalGas += gas[i];
            totalCost += cost[i];
            curGas += gas[i] - cost[i];
            
            if (curGas < 0) {
                start = i + 1;
                curGas = 0;
            }
        }
        
        return totalGas >= totalCost ? start : -1;
    }
}

Python 实现

class Solution:
    def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int:
        n = len(gas)
        total_gas = 0
        total_cost = 0
        cur_gas = 0
        start = 0
        
        for i in range(n):
            total_gas += gas[i]
            total_cost += cost[i]
            cur_gas += gas[i] - cost[i]
            
            if cur_gas < 0:
                start = i + 1
                cur_gas = 0
        
        return start if total_gas >= total_cost else -1

C++ 实现

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int n = gas.size();
        int totalGas = 0;
        int totalCost = 0;
        int curGas = 0;
        int start = 0;
        
        for (int i = 0; i < n; i++) {
            totalGas += gas[i];
            totalCost += cost[i];
            curGas += gas[i] - cost[i];
            
            if (curGas < 0) {
                start = i + 1;
                curGas = 0;
            }
        }
        
        return totalGas >= totalCost ? start : -1;
    }
};

执行结果

C# 实现

  • 执行用时:132 ms
  • 内存消耗:42.9 MB

Python 实现

  • 执行用时:52 ms
  • 内存消耗:17.9 MB

C++ 实现

  • 执行用时:4 ms
  • 内存消耗:68.9 MB

性能对比

语言执行用时内存消耗特点
C#132 ms42.9 MB执行速度适中,内存消耗适中
Python52 ms17.9 MB执行速度适中,内存消耗较低
C++4 ms68.9 MB执行速度最快,内存消耗较高

代码亮点

  1. 🎯 一次遍历解决问题,时间复杂度为O(n)
  2. 💡 巧妙利用数学推导,避免了暴力解法的高时间复杂度
  3. 🔍 同时计算总油量和总消耗量,快速判断是否有解
  4. 🎨 代码简洁清晰,逻辑易于理解

常见错误分析

  1. 🚫 没有考虑总油量小于总消耗量的情况,导致无解时仍返回起点
  2. 🚫 更新起点的逻辑错误,导致找不到正确的起点
  3. 🚫 没有正确处理环路的特性,导致结果错误
  4. 🚫 使用暴力解法时,没有正确模拟环路行驶,导致结果错误

解法对比

解法时间复杂度空间复杂度优点缺点
暴力法O(n²)O(1)思路直观,易于理解时间复杂度高,效率低
一次遍历O(n)O(1)时间复杂度低,效率高需要一定的数学推导
贪心算法(一次遍历的变种)O(n)O(1)时间复杂度低,效率高实现稍复杂

相关题目

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值