【经典面试题-LeetCode134:加油站问题(Java实现)】

一、题目描述

1.题目内容

在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组 gas 和 cost ,如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。
原题链接:https://leetcode.cn/problems/gas-station

在这里插入图片描述
上图描述了“环形”路线的含义,每个节点i表示加油站,其数值表示gas[i];节点之间的曲线表示从加油站i到加油站j之间的油耗,行驶过程必须顺时针进行。以节点1为起点,题目含义就是顺时针绕行,测试是否可以顺利回到起点。

2.样例

在这里插入图片描述

二、解决方案

1.分析

很直观的方案,依次测试每个加油站是否可以作为起点即可,但是这道题正向遍历暴力解很容易报超时,因为测试样例中有几个极其离谱的,后面我展示了一个,所以尝试从数组末尾反向遍历,依次尝试当前加油站是否可以作为起点,一旦遇到第一个可作为起点的加油站,就为最优解。

说一下我的想法:如果以任意站点为起点start,我们逐步行驶,只要可以到达起点的前一个站点end,并且此时汽油剩余量curGas>=cost[end],说明可以顺利回到起点。

以样例1为例,看一下我的解决思路:
在这里插入图片描述
(1)第一次,假设站点4(指数组索引)为起点,既start=4,车辆剩余油量curGas=g[start]=5:

  • 1)剩余油量为curGas=5>cost[start]=2,所以可以到达下一站点0,此时gas[0]=1,故车辆剩余油量为curGas=3=5-2+1;
  • 2)剩余油量为curGas=3>=cost[0]=3,所以可到达下一站点1,此时gas[1]=2,故车辆剩余油量为curGas=2=3-3+2;
  • 3)此时curGas=2<cost[1]=4,说明剩余油量无法到达下一站,也就是说以站点4为起点是无法完成环形一周的。因此测试前一个站点3是否可以做起点?
    在这里插入图片描述

(2)第二次,假设站点3(指数组索引)为起点,既start=3,车辆剩余油量curGas=g[start]=4:

  • 1)剩余油量为curGas=4>cost[start]=1,所以可以到达下一站点4,此时gas[4]=5,故车辆剩余油量为curGas=8=4-1+5;
  • 2)剩余油量为curGas=8>=cost[4]=2,所以可到达下一站点0,此时gas[0]=1,故车辆剩余油量为curGas=7=8-2+1;
  • 3)此时curGas=7>cost[0]=3,所以可到达下一站点1,此时gas[1]=2,故车辆剩余油量为curGas=6=7-3+2;
  • 4)此时curGas=6>cost[1]=4,所以可到达下一站点2,此时gas[2]=3,故车辆剩余油量为curGas=5=6-4+3,需要注意此时到达了终点end=start-1=2;
  • 5)此时curGas=5>=cost[2]=5,所以可顺利回到起点,因此以站点3为起点是合理的,此时停止遍历得最优解。

2.核心代码

class Solution{
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int n=gas.length;
        int i=n-1;  // 从后往前测试
        int start=-1;  // 为了保证没有找到起点返回-1的条件,初始化start=-1
        boolean isCan=false;  // 记录是否找到起点
        
        while (i>=0){
            if(gas[i]<cost[i]){  // 说明油量小于油耗,无法到达下一站,也就无法作为起点
                i--;
                continue;
            }
            start=i;  // 假设当前加油站为起点
            int end=start-1;  // 假设起点的前一站为终点
            if (start==0){  // 因为是环形路线,第一站的前一站为整个数组的最后一站
                end=n-1;
            }
            int curGas=gas[start];  // 测试以当前起点开始行驶,是否可以环绕一周
            while (true){
                if (curGas<cost[i]){  // 行驶过程中发现,当前汽油量无法到达下一各加油站
                    break;
                }
                if (i==end&&curGas>=cost[i]){  // 如果到达起点的前一个加油站时,发现汽油量足以到达起点,说明找到了合适的起点
                    isCan=true;
                    break;
                }
                curGas=curGas-cost[i%n]+gas[(i+1)%n];  //计算到达下一站后的汽油量(需要加上下一站的油量)
                i=(i+1)%n;  // 计算下一站
            }
            if (isCan){  // 一旦找到起点就停止遍历
                break;
            }
            i=start-1;  // 说明当前站无法作为起点,则测试前一个站是否可以作为起点
        }
        return isCan?start:-1;
    }
}

说实话这道题能暴力解出来,真的是有些运气的,人都做崩溃了。。下次再研究大佬们的题解吧。
在这里插入图片描述
题外话,这种样例是什么神仙想出来的啊,要我老命。。。
在这里插入图片描述

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NorthSmile

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值