【算法学习】加油站

题目

在一条环路上有 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<=10^5
  • 0 <= gas[i], cost[i] <= 10^4

题目解析

问题理解与抽象

明确问题本质:这是一个在环形路径上寻找可行起点的问题,需要判断是否存在一个起始加油站,使得汽车能够在不耗尽汽油的情况下绕环路行驶一周。
数据抽象:题目中给出了两个数组 gas 和 cost,gas[i] 表示第 i 个加油站的汽油储备,cost[i] 表示从第 i 个加油站到第 i + 1 个加油站的汽油消耗。汽车从某个加油站出发,初始油箱为空,且油箱容量无限。

可行性判断

整体可行性:首先要判断是否存在一个可行的出发站,即总油量是否足够行驶完整条环路。可以通过计算所有加油站的汽油总量和行驶全程的总消耗来判断。如果总汽油量小于总消耗,那么无论从哪个加油站出发,都无法绕环路行驶一周,直接返回 -1。
局部可行性:对于每个加油站,需要考虑从该加油站出发能否到达下一个加油站。如果从某个加油站出发后,在到达下一个加油站之前汽油耗尽,那么这个加油站就不能作为起始站。

解法

解法一(暴力解法)

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
    	for (int i = 0; i < gas.length;; i++) {
        	int current = i;
        	// 剩余油量
        	int remain = gas[i]; 
        	// 剩余油量能否抵达下一地点
        	while (remain - cost[current] >= 0) {
            	// 抵达后可以补给油
            	remain = remain - cost[current] + gas[(current + 1) % n];
            	current = (current + 1) % n;
            	//成功返回到原点
            	if (current == i) {
                	return i;
            	}
        }
    }
	// 无法抵达
	return -1;

因为循环套循环,时间复杂度为O(N^2)

解法二(优化后)

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int currentGas = 0; // 当前剩余油量
        int minGas = Integer.MAX_VALUE; // 最低油耗
        int minIndex = 0; // 最低油耗时的下标
        for(int i=0;i<gas.length;i++){
           currentGas += gas[i] - cost[i]; //当前加油站的补给 - 马上前往下一地点的油耗
           if(currentGas<minGas){ 
                minGas = currentGas;  // 调整最小油耗
                minIndex = i;  // 记录最小油耗的点,亏空最严重的一个点必须放在最后一步走,等着前面剩余的救助
           }
        }
        return currentGas<0?-1: (minIndex+1) % gas.length; // 当转完一圈,如果油耗小于0则无法转一圈; 反之,最小油耗点是最后一个点。
    }
}

时间复杂度为O(N),空间复杂度为O(1)。

解法三:官方解法

class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int n = gas.length;
        int i = 0;
        while (i < n) {
            int sumOfGas = 0, sumOfCost = 0;
            int cnt = 0;
            while (cnt < n) {
                int j = (i + cnt) % n;
                sumOfGas += gas[j];
                sumOfCost += cost[j];
                if (sumOfCost > sumOfGas) {
                    break;
                }
                cnt++;
            }
            if (cnt == n) {
                return i;
            } else {
                i = i + cnt + 1;
            }
        }
        return -1;
    }
}

官方写了一大堆表达式,试图把我们讲会……其实中心想法就一个,如果从X出发无法到达Y,那么X-Y中任意一点Z都无法到达Y。 因此我们要从不可到达点出发来推理是否可以走完一圈。

题目思考

考察点:

  • 贪心算法思想本题核心解法运用了贪心策略。在遍历过程中,一旦发现当前累计的油量不足以支撑到下一个加油站,就果断放弃当前起始点,将起始点更新为下一个加油站。这体现了贪心算法在每一步都做出当前看起来最优的选择,从而最终得到全局最优解的思想。
  • 逻辑推理与边界条件处理需要深入分析在不同情况下汽车能否完成环形行驶,特别是对于总油量和总消耗的关系判断,以及如何确定可行的起始点。同时,要考虑到数组越界等边界情况,因为是环形路线,所以要确保逻辑在循环过程中能正确处理首尾相连的情况。
  • 复杂度分析能力要能分析出不同解法的时间复杂度和空间复杂度。像暴力枚举法时间复杂度高达O(N^2),而优化后的贪心算法时间复杂度仅为 O(N),空间复杂度为 O(1),这考察了对算法效率的评估和优化能力。

适用题目类别:

  1. 连续子数组问题最大子数组和问题:给定一个整数数组,找出具有最大和的连续子数组。你可以采用类似本题的贪心策略,在遍历数组时,维护一个当前子数组的和,如果当前和小于 0,就重新开始一个新的子数组。例如,对于数组 [-2, 1, -3, 4, -1, 2, 1, -5, 4],通过不断更新当前子数组的和,最终可以找到最大和的连续子数组 [4, -1, 2, 1]。和为目标值的最短连续子数组:在一个正整数数组中,找出和大于等于给定目标值的最短连续子数组。可以在遍历过程中维护当前子数组的和,当和达到目标值时,尝试缩小子数组的长度,同时更新最短长度。
  2. 区间选择问题活动选择问题:有多个活动,每个活动有开始时间和结束时间,要求选择最多的互不冲突的活动。可以按照活动的结束时间进行排序,然后依次选择结束时间最早且与已选活动不冲突的活动,这也是贪心算法的应用,和本题通过不断选择最优起始点的思路类似。区间覆盖问题:给定一系列区间,要选择最少的区间来覆盖一个给定的大区间。可以对区间按起始点排序,然后在每个阶段选择能覆盖最大范围的区间,不断更新覆盖的范围,直至覆盖整个大区间。
  3. 资源分配问题电池供电问题:有多个设备,每个设备需要一定的电量才能正常工作,而电池的电量有限。要找出一种分配方案,使得尽可能多的设备能够正常工作。可以按照设备所需电量从小到大排序,依次分配电量,直到电池电量不足,这和本题中合理选择起始加油站以保证汽车能行驶全程的思路一致。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值