leetcode刷题日记-【134. 加油站】

174 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第33天,点击查看活动详情

题目描述

在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

给定两个整数数组 gas 和 cost ,如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。

 

示例 1:

输入: gas = [1,2,3,4,5], cost = [3,4,5,1,2] 输出: 3 解释: 从 3 号加油站(索引为 3 处)出发,可获得 4 升汽油。此时油箱有 = 0 + 4 = 4 升汽油 开往 4 号加油站,此时油箱有 4 - 1 + 5 = 8 升汽油 开往 0 号加油站,此时油箱有 8 - 2 + 1 = 7 升汽油 开往 1 号加油站,此时油箱有 7 - 3 + 2 = 6 升汽油 开往 2 号加油站,此时油箱有 6 - 4 + 3 = 5 升汽油 开往 3 号加油站,你需要消耗 5 升汽油,正好足够你返回到 3 号加油站。 因此,3 可为起始索引。 示例 2:

输入: gas = [2,3,4], cost = [3,4,3] 输出: -1 解释: 你不能从 0 号或 1 号加油站出发,因为没有足够的汽油可以让你行驶到下一个加油站。 我们从 2 号加油站出发,可以获得 4 升汽油。 此时油箱有 = 0 + 4 = 4 升汽油 开往 0 号加油站,此时油箱有 4 - 3 + 2 = 3 升汽油 开往 1 号加油站,此时油箱有 3 - 3 + 3 = 3 升汽油 你无法返回 2 号加油站,因为返程需要消耗 4 升汽油,但是你的油箱只有 3 升汽油。 因此,无论怎样,你都不可能绕环路行驶一周。  

提示:

  • gas.length == n
  • cost.length == n
  • 1 <= n <= 105
  • 0 <= gas[i], cost[i] <= 104

解题思路

给定一个数组gas代表每个位置能获取到的汽油数量,数组cost代表到达每个位置需要花费的汽油数量。求从第几个位置出发能走完数组长度,返回出发位置,不存在的话直接返回-1;

设gas长度为length,可以把它想象成一个环形,环形内的节点数为length。出发点在环形上的位置不固定,但是它的顺序一定是按照顺时针移动的。

所以这道题并不是重新排列所有的节点,而是寻找某一个固定节点之后向后依次遍历节点,最终判断能否和头节点相遇。

首先,解题思路肯定是遍历数组内的元素,假定它们为头节点,然后依次向后看是否满足条件。但是这样循环的话会超时,所以要缩小需要遍历的元素范围

重点就是缩小范围的思路。我们可以从数组中排除一定不会是起点的元素。

假定从位置x是能到达y的,那么位置x、y之间的任意节点的汽油总获得量一定是大于等于汽油总的消耗量,即有式子

①∑gas(x->i) >= ∑cost(x->i)(i为区间x、y之间的任一节点);

如果从位置x无法到达位置y,则一定有x、y之间总共获得的汽油小于汽油总的消耗量(单个节点可能大于或等于),即有式子

①∑gas(x->y) < ∑cost(x->y) (x到y的区间和);

现在假定从位置x能到达位置y,但是从x到达不了位置y+1,那么现在就需要确认是不是x、y区间内所有的位置都无法到达位置y+1

如果是的话就可以将[x,y]从遍历的范围内筛出,直接从y+1的位置开始下次循环。

假定i为区间[x,y]内的一个位置(i>x)

①∑gas(i->y+1)= ∑gas(x->y+1)-∑gas(x->i-1);

②因为∑gas(x->y+1) < ∑cost(x->y+1);

③所以∑gas(i->y+1) < ∑cost(x->y+1)-∑gas(x->i-1);

又因为i是在[x,y]内,且x可以到y,且i>x,所以i-1肯定也在区间内(区间),所以∑gas(x->i-1) >= ∑cost(x->i-1);

④所以∑cost(x->y+1)-∑cost(x->i-1) >= ∑cost(x->y+1)-∑gas(x->i-1);

所以③可以变成∑gas(i->y+1) < ∑cost(x->y+1)-∑cost(x->i-1);

最终∑gas(i->y+1) < ∑cost(i->y+1);

由此可见,在区间[x,y]内的所有元素都无法从它们除法到达位置y+1,所以可以直接跳过这些元素。

所以每次循环可以从中找到第一个无法到达下个位置的元素,从它开始进行下次循环

代码实现

/**
 
 */
public static int canCompleteCircuit(int[] gas, int[] cost) {
    int length = gas.length;
    // 假设出发节点为i
    int i = 0;
    while (i < length) {

        int sumGas = 0;
        int sumCost = 0;
        // 已经过的节点个数
        int count = 0;
        while (count < length) {
            // 当前位置,因为是环状的,所以要用取模的方式获取下标
            int index = (i + count) % length;
            sumGas += gas[index];
            sumCost += cost[index];
            if (sumCost > sumGas) {
                // 进行下次循环
                i = i + count + 1;
                break;
            }
            count ++;
        }
        if (count == length) {
            return i;
        }
    }
    return -1;
}