• 【经典面试题-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 在这里插入图片描述

    (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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

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

  • 相关阅读:
    CSP 202005-1 重复局面
    【Linux】——初识程序地址空间
    USB到UART桥接控制器——GP232RNL
    1055 集体照(测试点3, 4, 5)
    江苏MES
    顶级玩家:一招搞定 App 自动化老大难问题
    快速体验Spring Boot了解使用、运行和打包 | SpringBoot 2.7.2学习系列
    RabbitMQ实践——最大长度队列
    【数据结构】——链表经典OJ(leetcode)
    需求着急上线,是写烂代码的理由吗?
  • 原文地址:https://blog.csdn.net/qq_43665602/article/details/127642973