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

加油站问题

  • 一、题目描述
    • 1.题目内容
    • 2.样例
  • 二、解决方案
    • 1.分析
    • 2.核心代码

一、题目描述

1.题目内容

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

【经典面试题-LeetCode134:加油站问题(Java实现)】_第1张图片
上图描述了“环形”路线的含义,每个节点i表示加油站,其数值表示gas[i];节点之间的曲线表示从加油站i到加油站j之间的油耗,行驶过程必须顺时针进行。以节点1为起点,题目含义就是顺时针绕行,测试是否可以顺利回到起点。

2.样例

【经典面试题-LeetCode134:加油站问题(Java实现)】_第2张图片

二、解决方案

1.分析

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

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

以样例1为例,看一下我的解决思路:
【经典面试题-LeetCode134:加油站问题(Java实现)】_第3张图片
(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 【经典面试题-LeetCode134:加油站问题(Java实现)】_第4张图片

(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;
    }
}

说实话这道题能暴力解出来,真的是有些运气的,人都做崩溃了。。下次再研究大佬们的题解吧。
【经典面试题-LeetCode134:加油站问题(Java实现)】_第5张图片
题外话,这种样例是什么神仙想出来的啊,要我老命。。。
【经典面试题-LeetCode134:加油站问题(Java实现)】_第6张图片

你可能感兴趣的:(LeetCode,leetcode,java,算法,数据结构,贪心算法)