欢迎访问LeetCode题解合集
题目描述
在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i]
升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i]
升。你从其中的一个加油站出发,开始时油箱为空。
如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。
说明:
- 如果题目有解,该答案即为唯一答案。
- 输入数组均为非空数组,且长度相同。
- 输入数组中的元素均为非负数。
示例 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 可为起始索引。
示例 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 升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。
题解:
法一:
双指针。
暴力解法是二重循环,第一重循环枚举起点,第二重循环判断能否跑完一圈,考虑怎么减小枚举的起点数目。
假设从 st
出发,最远能到达 ed
,那么从 [st + 1 ~ ed]
中某个点能否环绕一圈呢?答案是 否定 ,反证法证明:
假设从 st + 1
开始能环绕一圈,说明 st + 1
一定能到达 ed + 1
,因为 st
能到达 st + 1
,所以 st
也能到达 ed + 1
,产生矛盾。
所以下一个起点不用从 st + 1
开始,直接从 ed + 1
开始。
注意:处理 ed < st
的情况。
时间复杂度:$O(n)$
额外空间复杂度:$O(1)$
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int n = gas.size();
int rest;
for ( int i = 0, j; i < n; ++i ) {
if ( gas[i] < cost[i] ) continue;
j = i;
rest = gas[i];
while ( rest >= cost[j] ) {
rest += gas[(j + 1) % n] - cost[j];
j = (j + 1) % n;
if ( j == i ) return i;
}
if ( j < i ) return -1;
i = j;
}
return -1;
}
};
/*
时间:4ms,击败:98.65%
内存:9.6MB,击败:93.07%
*/
另外一个版本的双指针:
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int n = gas.size();
vector<int> f( n << 1 );
for ( int i = 0; i < n; ++i ) {
f[i] = f[i + n] = gas[i] - cost[i];
}
int st, ed, sum = 0;
int r = n << 1;
for ( int i = 0; i < n; ++i ) {
if ( f[i] < 0 ) continue;
sum = 0;
st = ed = i;
while ( ed < r ) {
sum += f[ed];
if ( sum < 0 ) while ( sum < 0 ) sum -= f[st++];
if ( ed - st + 1 == n ) return st;
++ed;
}
i = ed;
}
return -1;
}
};
/*
时间:4ms,击败:98.65%
内存:9.8MB,击败:86.32%
*/
法二:
贪心。
一个结论:总加油量 >= 总耗油量 一定有解。
-
假设从
0
开始,累加每个站剩余油量rest += gas - cost
,如果到达i
时rest < 0
,说明[0,i]
都不能作为起点(法一中已证明),从i + 1
开始; -
如果到达
j
时rest < 0
,说明[i + 1, j]
都不能作为起点,从j + 1
开始; -
这个过程不可能一直持续下去,因为 总加油量 >= 总耗油量,所以一定有一段能够补齐前面缺少的油量。
时间复杂度:$O(n)$
额外空间复杂度:$O(1)$
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int rest = 0, sum = 0;
int st = 0;
for ( int i = 0; i < gas.size(); ++i ) {
rest += gas[i] - cost[i];
sum += gas[i] - cost[i];
if ( sum < 0 ) {
sum = 0;
st = i + 1;
}
}
return rest >= 0 ? st : -1;
}
};
/*
时间:4ms,击败:98.65%
内存:9.4MB,击败:99.54%
*/