在一条环路上有 N
个加油站,其中第 i
个加油站有汽油 gas[i]
升。
你有一辆油箱容量无限的的汽车,从第 i
个加油站开往第 i+1
个加油站需要消耗汽油 cost[i]
升。你从其中的一个加油站出发,开始时油箱为空。如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。
我们先来看下暴力法怎么做。
暴力,无非就是以每个加油站为起点,然后进行遍历,看看绕一圈之后,是否中途有断油的情况,若没有,说明这个起点是我们要的结果。
public int canCompleteCircuit(int[] gas, int[] cost) {
for (int i = 0; i < gas.length; i++) {
// 以当前加油站为起点,算出初始油量
int restCapacity = gas[i] - cost[i];
// 下一个加油站位置
int index = (i + 1) % cost.length;
// 开始绕一圈,要求不能断油
while (restCapacity > 0 && index != i) {
restCapacity += gas[index] - cost[index];
index = (index + 1) % cost.length;
}
// 如果跑了一圈,回到起点,并且油有剩余就说明OK的
if (restCapacity >= 0 && index == i) {
return i;
}
}
return -1;
}
但是暴力法肯定是不行的,因为暴力法也就是穷举,这里的时间复杂度是O(N²)
,因此很容易超时。因此我们需要降低时间复杂度。
那么我们来分析一下。
gas[i] - cost[i]
为负数。 那么以它为起点肯定是不行的了,也无需往后遍历下去。gas[i] - cost[i]
为正数。代表可以走到下标为2的加油站的。>=0
。因此我们可以这么做:
totalRest
变量,计算跑完一圈时候的剩余油量。curRest
变量,计算当前剩余油量。startIndex
作为起点下标。curRest<0
,说明之前的位置都不能作为起点,那么更新startIndex
为下一个加油站。那么最终代码就是:
public int canCompleteCircuit(int[] gas, int[] cost) {
int totalRest = 0;// 跑完一圈剩余油量
int curRest = 0;// 当前剩余油量
int startIndex = 0;// 起点下标
for (int i = 0; i < gas.length; i++) {
totalRest += gas[i] - cost[i];
curRest += gas[i] - cost[i];
// 如果发生了断油,说明在[i,startIndex]这区间的加油站,都不能作为起点
if (curRest < 0) {
// 更新起点 和当前剩余油量
startIndex = i + 1;
curRest = 0;
}
}
// 如果说总加油量 < 总耗油量,那么永远不可能走完一圈
if (totalRest < 0) {
return -1;
}
return startIndex;
}