题目描述
在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组 gas 和 cost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -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 升汽油,正好足够你返回到 3 号加油站。因此,3 可为起始索引。
解决方案
方式一:贪心
假设我们此前发现,从加油站 x 出发,每经过一个加油站就加一次油(包括起始加油站),最后一个可以到达的加油站是 y(不妨设 x<y)
第一个式子表明从x加油站出发无法到达加油站 y 的下一个加油站,
第二个式子表明可以到达 y 以及 y 之前的所有加油站。
现在,考虑任意一个位于 x,y 之间的加油站 z(包括 x 和 y),我们现在考察从该加油站出发,能否到达加油站 y 的下一个加油站
结论 为,如果从 加油站 x 出发无法到达 加油站 y ,则 x 和 y 之间的加油站也无法到达加油站 y
实现代码
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int n = gas.length;//加油站的数量
int i = 0;
while (i < n) {//外层索引,判断从第i个站点作为起点,能否绕圈完成行程
//sumOfGas总的加油量 sumOfCost 总的耗油量
int sumOfGas = 0, sumOfCost = 0;
int cnt = 0; //经过的站点数量
while (cnt < n) {
int j = (i + cnt) % n; //对应的加油站的索引
sumOfGas += gas[j];
sumOfCost += cost[j];
if (sumOfCost > sumOfGas) {
break;//如果耗油量大于加油量,则不能实现绕圈
}
cnt++;
}
if (cnt == n) {
return i; //如果能绕圈的话,返回下标
} else {
i = i + cnt + 1;//根据结论,第i个加油站,无法到达第cnt个加油站,那么之间的加油站也是无法到达i+cnt个加油站的,所以直接从第i+cnt+1个加油占出发
}
}
return -1;
}
}
复杂度分析
时间复杂度:O(N),
其中 N 为数组的长度。我们对数组进行了单次遍历。
空间复杂度:O(1)。
方式二: 计算剩余油量
算法思想: 统计经过i个站点的剩余的油量,并且记录剩余油量最小的站点,如果剩余油量
实现代码
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int len = gas.length;
int spare = 0;//遍历站点累积剩余的汽油量。
int minSpare = Integer.MAX_VALUE;//遍历站点之后的最小剩余汽油量
int minIndex = 0;//出现最小剩余汽油量时,对应的站点
for (int i = 0; i < len; i++) {
spare += gas[i] - cost[i];// 剩余的油量
if (spare < minSpare) {
minSpare = spare; // 最小的剩余油量
minIndex = i;
}
}
return spare < 0 ? -1 : (minIndex + 1) % len;
}
}
spare < 0 ? -1 : (minIndex + 1) % len;
spare < 0:如果经过完整一轮遍历后,总的剩余汽油量 spare 是小于 0 的,这意味着在整个环形路线中,无论从哪个站点出发,按照给定的各站点汽油量和消耗情况,最终汽油都是不够用的,没办法绕一圈回到起点,所以此时返回 -1,表示不存在这样能完成环形路线的起始站点。
而如果 spare >= 0,说明整体上汽油量是足够绕一圈的。此时需要返回合适的起始站点索引,这里返回 (minIndex + 1) % len。之所以是 minIndex + 1,是因为我们记录的 minIndex 是出现最小剩余汽油量的那个站点索引
而从逻辑上分析,想要顺利绕圈,应该从这个最小剩余汽油量站点的下一个站点出发(因为在这个最小剩余站点处汽油量是最少的,往前汽油更少,所以往后一个站点出发更有可能绕圈成功),同时使用取余操作 % len 是因为这是环形路线,当 minIndex + 1 的值超过了站点数量 len 时,通过取余可以将索引正确地映射回环形路线中的有效站点索引范围,确保返回的是一个合法的可以作为起始站点的索引值。