要解决这个问题,我们可以使用贪心算法。我们需要从每个加油站出发,判断是否能够绕环形路线行驶一圈。以下是解决思路和代码实现。

思路

  1. 初始化变量:定义两个变量,total_tank 用于记录总体的汽油差(总的油量减去总的消耗),current_tank 用于记录当前油量差;还需要一个 start 变量记录可能的起始加油站下标。
  2. 遍历加油站:循环遍历每个加油站,计算从当前加油站出发到下一个加油站的油量差。
  3. 判断可行性: 如果 current_tank 小于 0,说明从 start 到当前加油站不可行,更新 start 为当前加油站的下一个,并重置 current_tank。
  4. 最终判断:如果 total_tank 大于或等于 0,说明有解,返回 start;否则返回 -1。

代码实现

#include <vector>

class Solution {
public:
    int canCompleteCircuit(std::vector<int>& gas, std::vector<int>& cost) {
        int total_tank = 0;
        int current_tank = 0;
        int start = 0;

        for (int i = 0; i < gas.size(); ++i) {
            total_tank += gas[i] - cost[i];
            current_tank += gas[i] - cost[i];

            // If current tank is negative, we can't start from 'start'
            if (current_tank < 0) {
                start = i + 1; // Try the next station
                current_tank = 0; // Reset current tank
            }
        }

        return (total_tank >= 0) ? start : -1;
    }
};

示例用法

#include <iostream>

int main() {
    Solution solution;
    std::vector<int> gas = {2, 3, 1};
    std::vector<int> cost = {3, 1, 2};
    
    int result = solution.canCompleteCircuit(gas, cost);
    std::cout << result << std::endl; // 输出: 1
    return 0;
}

总结

该算法的时间复杂度是 O(N),空间复杂度是 O(1),通过一次遍历判断可行性,确保了高效性。

在算法中,start = i + 1 的意思是,当你发现当前的 current_tank 变成负值时,说明从 start 到加油站 i 这段路无法完成。因此,下一次尝试从 i + 1 开始。

解释

  1. 当前加油站的油量不足:如果从 start 到 i 的过程中,油量不足以到达 i + 1,那么可以确定,从 start 到 i 的路径无法完成。
  2. 下一站的选择:既然从 start 到 i 失败,所有的加油站 start 到 i 之间的油量加消耗,必须在这段过程中一直低于 current_tank,所以无法从这些加油站出发。因此,我们可以将起始点 start 更新为 i + 1,尝试从下一个加油站开始。
  3. 为什么不是其他的:选择 i + 1 是因为你已经判断过了从 start 到 i 之间的加油站无法提供足够的油量,因此任何 start 到 i 之间的加油站都不适合再作为起始点。

举例说明

假设有以下油量和消耗:

  • gas = [1, 2, 3]
  • cost = [2, 3, 1]

遍历过程:

  1. 从加油站 0 开始:current_tank 计算为 1 - 2 = -1,所以不能到达 1。更新 start = 1。
  2. 从加油站 1 开始:current_tank 计算为 2 - 3 = -1,所以不能到达 2。更新 start = 2。
  3. 从加油站 2 开始:current_tank 计算为 3 - 1 = 2,可以到达 0。

最后,如果 total_tank >= 0,返回 start

总结

通过这种方式,算法有效地排除了不可能的起始点,保证了我们能找到合适的起点(如果存在)。

在这个算法中,虽然我们将起始点更新为 i + 1,但实际上不会漏掉检测 i + 1 之前的点是否可达。这里有几个原因:

1. 通过 current_tank 进行判断

在遍历过程中,current_tank 用于动态地跟踪从当前起始点到当前加油站 i 的油量差。如果在某个加油站 i 的时候,current_tank 变成负值,这就意味着从 starti 之间的所有加油站都不可能成为有效的起点。

2. 更新起始点的逻辑

当我们将 start 更新为 i + 1 时,算法已经排除了从 starti 之间的所有加油站作为起点的可能性。下一轮的起点会是 i + 1,并且从这个新起点开始的所有加油站都会重新计算 current_tank,确保检测到新路径的可行性。

3. 全面的遍历

由于我们对所有的加油站进行了遍历,一旦循环结束,我们会检查 total_tank。这个总油量差可以确保我们能覆盖到所有可能的路径。因此,即使某些加油站在上一次循环中被排除,但它们会在后续的循环中被重新检查。

例子分析

假设有以下数据:

  • gas = [1, 2, 3, 4]
  • cost = [3, 4, 3, 2]
  1. 从加油站 0 开始:current_tank 计算为 1 - 3 = -2,因此 start 更新为 1。
  2. 从加油站 1 开始:current_tank 计算为 2 - 4 = -2,因此 start 更新为 2。
  3. 从加油站 2 开始:current_tank 计算为 3 - 3 = 0,可以继续尝试加油站 3。从加油站 3 计算后,发现可以回到 0。

结论

这种方式确保了不会漏掉任何点,因为每次尝试新的起始点时,都会重新从新起点开始计算油量和消耗。通过不断更新 start 和检查 current_tank,我们能有效地找到有效的起始点。如果最终的 total_tank 大于或等于 0,则说明存在一个完整的回路。