134. 加油站
题目描述
在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组 gas 和 cost ,如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。
解题思路
思路一:暴力解法
依次考虑从第 n 个点出发,能否回到第 n 个点。
由于是个圆,得到下一个点的时候我们需要取余数。
实现代码如下:
/**
* @param {number[]} gas
* @param {number[]} cost
* @return {number}
*/
var canCompleteCircuit = function(gas, cost) {
// let len = gas.length;
// //考虑从每一个点出发
// for (let i = 0; i < len; i++) {
// let j = i,
// remain = gas[i];
// // 当前剩余的油能否到达下一个点
// while (remain - cost[j] >= 0) {
// // 减去花费的加上新的点的补给
// remain = remain - cost[j] + gas[(j + 1) % len];
// j = (j + 1) % len;
// //j 回到了 i
// if (j == i) {
// return i;
// }
// }
// }
// //任何点都不可以
// return -1;
};
但该解法会提示超出时间限制。
我们继续优化。
我们记录 i
能到达的最远站点为j
,且 j < i, 则i + 1
到j
之间的节点都不可能绕一圈了。 所以i
后边的站点就不需要考虑了,直接返回-1即可.
实现代码如下:
/**
* @param {number[]} gas
* @param {number[]} cost
* @return {number}
*/
var canCompleteCircuit = function(gas, cost) {
let len = gas.length;
//考虑从每一个点出发
for (let i = 0; i < len; i++) {
let j = i,
remain = gas[i];
// 当前剩余的油能否到达下一个点
while (remain - cost[j] >= 0) {
// 减去花费的加上新的点的补给
remain = remain - cost[j] + gas[(j + 1) % len];
j = (j + 1) % len;
//j 回到了 i
if (j == i) {
return i;
}
}
//最远距离绕到了之前,所以 i 后边的都不可能绕一圈了
if (j < i) {
return -1;
}
//i 直接跳到 j,外层 for 循环执行 i++,相当于从 j + 1 开始考虑
i = j;
}
//任何点都不可以
return -1;
};
思路二:一次遍历
前提:
首先判断总油量是否小于总油耗,如果是则肯定不能走一圈;大于或等于则一定有解,且保证有唯一解。
当题目有解时:
在每个加油站点 i,我们可以记录汽车的当前油量(可为负值)为 currentGas = currentGas - cost[i] + gas[i]
。若 currentGas < 0
,则汽车无法到达下一个站点 i+1。
若汽车从 i 出发(出发前实际油量为 0)恰好只能到达 j,而无法到达 j+1,则有到达j+1
前的currentGas > 0
均成立。我们可以发现,此时从 i+1, i+2, …, j−1 出发的车也都无法到达 j+1。因此汽车必须重新考虑从 j+1 出发才行。若 j+1 以后汽车在每个站点的当前油量currentGas 均 ≥ 0, 则 j + 1即是可行的出发点。
让汽车从站点 0 出发,当遇到currentGas < 0
时, 我们让汽车重新从 i+1 出发。由于 sum(gas) >= sum(cost),因此肯定存在一个满足题意的解使得汽车能够到达站点 n−1,也能回到出发点。
实现代码如下:
/**
* @param {number[]} gas
* @param {number[]} cost
* @return {number}
*/
var canCompleteCircuit = function(gas, cost) {
let totalGas = 0, totalCost = 0;
for (let i = 0; i < gas.length; i++) {
totalGas += gas[i];
totalCost += cost[i];
}
// 总汽油 小于 总耗油量 返回-1
if (totalGas < totalCost) {
return -1;
}
let currentGas = 0, start = 0;
for (let i = 0; i < gas.length; i++) {
currentGas = currentGas - cost[i] + gas[i];
// 在i处的油量<0,说明从之前站点出发的车均无法到达i
if (currentGas < 0) {
// 重新出发时油量置为0
currentGas = 0;
// 尝试从下一个站点i+1重新出发
start = i + 1;
}
}
return start;
};
- 时间复杂度:O(n),其中 n 为加油站数;
- 空间复杂度:O(1);
参考资料
https://leetcode.cn/problems/gas-station/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by–30/
https://leetcode.cn/problems/gas-station/solution/by-flix-fhpm/