要解决这个问题,我们可以使用贪心算法。我们需要从每个加油站出发,判断是否能够绕环形路线行驶一圈。以下是解决思路和代码实现。
思路
- 初始化变量:定义两个变量,
total_tank
用于记录总体的汽油差(总的油量减去总的消耗),current_tank
用于记录当前油量差;还需要一个start
变量记录可能的起始加油站下标。 - 遍历加油站:循环遍历每个加油站,计算从当前加油站出发到下一个加油站的油量差。
- 判断可行性: 如果 current_tank 小于 0,说明从 start 到当前加油站不可行,更新 start 为当前加油站的下一个,并重置 current_tank。
- 最终判断:如果
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
开始。
解释
- 当前加油站的油量不足:如果从 start 到 i 的过程中,油量不足以到达 i + 1,那么可以确定,从 start 到 i 的路径无法完成。
- 下一站的选择:既然从 start 到 i 失败,所有的加油站 start 到 i 之间的油量加消耗,必须在这段过程中一直低于 current_tank,所以无法从这些加油站出发。因此,我们可以将起始点 start 更新为 i + 1,尝试从下一个加油站开始。
- 为什么不是其他的:选择 i + 1 是因为你已经判断过了从 start 到 i 之间的加油站无法提供足够的油量,因此任何 start 到 i 之间的加油站都不适合再作为起始点。
举例说明
假设有以下油量和消耗:
gas = [1, 2, 3]
cost = [2, 3, 1]
遍历过程:
- 从加油站 0 开始:current_tank 计算为 1 - 2 = -1,所以不能到达 1。更新 start = 1。
- 从加油站 1 开始:current_tank 计算为 2 - 3 = -1,所以不能到达 2。更新 start = 2。
- 从加油站 2 开始:current_tank 计算为 3 - 1 = 2,可以到达 0。
最后,如果 total_tank >= 0
,返回 start
。
总结
通过这种方式,算法有效地排除了不可能的起始点,保证了我们能找到合适的起点(如果存在)。
在这个算法中,虽然我们将起始点更新为 i + 1
,但实际上不会漏掉检测 i + 1
之前的点是否可达。这里有几个原因:
1. 通过 current_tank
进行判断
在遍历过程中,current_tank
用于动态地跟踪从当前起始点到当前加油站 i
的油量差。如果在某个加油站 i
的时候,current_tank
变成负值,这就意味着从 start
到 i
之间的所有加油站都不可能成为有效的起点。
2. 更新起始点的逻辑
当我们将 start
更新为 i + 1
时,算法已经排除了从 start
到 i
之间的所有加油站作为起点的可能性。下一轮的起点会是 i + 1
,并且从这个新起点开始的所有加油站都会重新计算 current_tank
,确保检测到新路径的可行性。
3. 全面的遍历
由于我们对所有的加油站进行了遍历,一旦循环结束,我们会检查 total_tank
。这个总油量差可以确保我们能覆盖到所有可能的路径。因此,即使某些加油站在上一次循环中被排除,但它们会在后续的循环中被重新检查。
例子分析
假设有以下数据:
gas = [1, 2, 3, 4]
cost = [3, 4, 3, 2]
- 从加油站 0 开始:current_tank 计算为 1 - 3 = -2,因此 start 更新为 1。
- 从加油站 1 开始:current_tank 计算为 2 - 4 = -2,因此 start 更新为 2。
- 从加油站 2 开始:current_tank 计算为 3 - 3 = 0,可以继续尝试加油站 3。从加油站 3 计算后,发现可以回到 0。
结论
这种方式确保了不会漏掉任何点,因为每次尝试新的起始点时,都会重新从新起点开始计算油量和消耗。通过不断更新 start
和检查 current_tank
,我们能有效地找到有效的起始点。如果最终的 total_tank
大于或等于 0,则说明存在一个完整的回路。