LeetCode 134. 加油站

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 + 1j之间的节点都不可能绕一圈了。 所以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/

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值