LeetCode琅琊榜第九层-加油站问题(图表法)

LeetCode134,加油站

难度:中等

博主空间与往期力扣

LeetCode琅琊榜第九层-加油站问题(图表法)_第1张图片

LeetCode琅琊榜第九层-加油站问题(图表法)_第2张图片

题目链接


目录

作者原始思路

"隐藏式"贪心算法 

思想与代码简述

问题

反省

最优易解法-图表法

案例分析

算法思想

代码实现

代码分析

结论


作者原始思路

"隐藏式"贪心算法 

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        var isReach = 0;
        var begin = 0;
        for (int i = 0; i < gas.length; i++) {
            isReach += (gas[i] - cost[i]);
        }
        if (isReach < 0) {
            return -1;
        }else {
            for (int i = 0; i < gas.length; i++) {
                var temp = 0;
                for (int j = i; j < gas.length; j++) {
                    temp += (gas[j] - cost[j]);
                    if (j == i && temp <= 0) {
                        break;
                    }
                }
                if (temp > isReach) {
                    begin = i;
                    isReach = temp;
                }
            }
            return begin;
        }
    }
}

思想与代码简述

  • 首先,明确一个概念,如果一辆车能走完全程,即全部的加油站都路过一次,那么说明在这个过程中所加的油一定大于他所消耗的油
  • 其次,策略是我们最开始走的时候应该处于加油量永远大于消耗量的加油站,即盈余油量的最初位置,这是我们贪心的方向
    • 1.同时遍历两个数组,将他们做差,就可以得出走到某个地方的油量
    • 2.用最终的油量进行比较,若大于0,说明走完还有油量,可以进行进一步判断,否则说明不能走完
    • 3.贪心的步骤就是从第i个位置开始,将后面的所有油量差做和,求出总和,再算出最大的位置,从而得出最优的盈余位置
    • 4.如果一开始就是负数,说明该位置不可能达到最大,可以立即退出

问题

LeetCode琅琊榜第九层-加油站问题(图表法)_第3张图片

反省

  • 有时候遇到这种题目,有时候真的没有想到有这么极端的案例,所以,我们要多刷题,优化自己的算法,争取通过全部案例
  • 这里有个有趣的现象,就是我使用贪心算法的时候,其实我不知道自己用了贪心算法,但是后来细想了一下才直到,所以,贪心算法内化于心最重要
  • 遇到这种题目,显然又要用一些奇奇怪怪的算法,隆重推出图表法

最优易解法-图表法

案例分析

  • gas = [1,2,3,4,5]
  • cost = [3,4,5,1,2]
  • 作图得
  • LeetCode琅琊榜第九层-加油站问题(图表法)_第4张图片
    • 作图技巧
      • 1.根据每一个gas和costs画出各个加油站的盈余情况,将盈余做累加,描绘出曲线的点 
      • 2.将各个点连接起来,获得油量变化曲线图
      • 3.应该把第一个点前面的那一段画起来,具有连贯性,且(0,0)这个点表示的是5,从而从图示上代表了公路形成一个有效的圆环,与题目相互对应,对后面的代码理解具有及其重要的作用

算法思想

  • 1.根据图示得,开始时的油量为负数,最终的油量为0,所以能到达,若最后的油量小于0,说明油量不足消耗量,不可能到达
  • 2.由生活实际得,我们从起点到终点,应该保证油量永远都大于等于0,否则无法到达
  • 3.上述图示是以1为起点,一次到达加油站[2,3,4,5]的,我们发现,其油量曲线永远小于0
  • 4.由于盈余情况是肯定的,所以曲线的增减趋势也是一定的,所以我们应该要处于最下面的点去到>=0的位置,这就保证了所有的点都在0的上面
  • 5.所以,我们找出最小的那一个点,以他作为开始的位置,保证了所有的点都在0上方

代码实现

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        var minGas = Integer.MAX_VALUE;
        var minGasIndex = 0;
        var curGas = 0;
        var len = gas.length;
        for (int i = 0; i < len; i++) {
            curGas += gas[i] - cost[i];
            if (curGas < minGas) {
                minGas = curGas;
                minGasIndex = i;
            }
        }
        return curGas < 0 ? -1 : (minGasIndex + 1) % len;
    }
}

代码分析

  • 1.变量分析
    • 1.1minGas 油量曲线图中,最下面的点
    • 1.2minGasIndex 油量曲线图中最下面点对应的下标
    • 1.3curGas 油量曲线图中的点 
    • 1.4数组长度
  • 2.遍历
    • 2.1获取当前当前盈余值(gas[i] - const[i]),并作累加获取curGas
    • 2.2获取最小值及其下标
  • 3.如果curGas < 0,说明消耗量大于油量,返回-1,否则返回( (minGasIndex + 1) % len)
    • 解释
      • 因为我们要让最小的那个点在0的上方,所以,所以不妨把他看成是图示中的(0,0)点,图示中表示5,那么这里表示最小值对应的点,
      • 根据思路,我们是从下一个位置开始走的,所以,开始位置是(minGasIndex + 1),即上述最小的那个点要最后才可以到达
      • 对于取模的思路
        • 如果示例为[3,1,1] [1,2,2]根据算法和图示,可以得出从1开始其所有的curGas都是大于0的,所以1就是正确位置,但是,算法的结果是3,所以取模的话刚好回到0,

结论

        这个图解法我觉得是相对于官方算法更为直观和容易理解的一个算法,官方用的数学累加,懒得看,不直观,不推荐,不过,这个图解法更加的抽象,相对对于一部分人而言更加难理解,但是,这种算法是给人眼前一亮的,所以,我非常的推荐,我来总结一下几点

        1.(0,0)这个点的含义,以及后面的应用

                2.图示每一个元素的意义

                        3.如何用表格推出算法

你可能感兴趣的:(LeeCode与算法,算法,leetcode)