当前位置: 首页 > news >正文

学习记录:js算法(七十五): 加油站

文章目录

    • 加油站
      • 思路一
      • 思路二
      • 思路三
      • 思路四
      • 思路五

加油站

在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组 gascost ,如果你可以按顺序绕环路行驶一周,则返回出发时加油站的编号,否则返回 -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 升汽油。
因此,无论怎样,你都不可能绕环路行驶一周。

思路一

function canCompleteCircuit(gas, cost) {let totalGas = 0, totalCost = 0, start = 0, tank = 0;for (let i = 0; i < gas.length; i++) {totalGas += gas[i];totalCost += cost[i];tank += gas[i] - cost[i];// 如果当前油量小于0,说明从start点出发无法到达下一个加油站if (tank < 0) {start = i + 1; // 更新起始点tank = 0;      // 清空油箱}}// 如果总的汽油量小于总的消耗量,无法完成环路if (totalGas < totalCost) {return -1;}return start;
}

讲解
可以通过贪心算法解决,主要的思路是寻找一个起始点,从这个点出发,汽车能够通过不断加油并且不耗尽油箱中的油,完成整个环路的行驶。下面详细介绍解题思路和代码实现。

  1. 初始化变量:totalGastotalCost 分别用来累计所有加油站的汽油总量和消耗的汽油总量。start 用来记录可能的起始加油站的索引,tank 则记录当前油箱里的油量。
  2. 遍历加油站:从第一个加油站开始遍历,对于每个加油站,做以下操作:
    ○ 更新油箱里的油量:tank += gas[i] - cost[i]
    ○ 如果油箱里的油量小于**0,说明从当前的 start 点出发无法到达下一个加油站,因此重置 start 点为下一个加油站的位置,并清空油箱:**start = i + 1tank = 0
    ○ 累计总汽油量和总消耗量:totalGas += gas[i] 和 totalCost += cost[i]
  3. 判断能否完成环路:如果 totalGas < totalCost,说明总的汽油量不足以完成整个环路的行驶,返回 -1。否则,从 start 点出发一定可以完成环路,返回 start

思路二

var canCompleteCircuit = function (gas, cost) {const n = gas.length;for (let start = 0; start < n; start++) {let totalGas = 0;let totalCost = 0;let canComplete = true;for (let i = 0; i < n; i++) {const index = (start + i) % n;totalGas += gas[index];totalCost += cost[index];if (totalGas < totalCost) {canComplete = false;break;}}if (canComplete) {return start;}}return -1;
};

讲解
这段代码定义了一个函数 canCompleteCircuit,用于判断在一个环形路线上,是否可以从某个加油站出发,顺利完成一圈而不耗尽汽油。

  1. 输入参数:函数接收两个数组 gascost,分别表示每个加油站的汽油量和从一个加油站到下一个加油站所需的汽油量。
  2. 获取加油站数量:通过 gas.length 获取加油站的数量,并存储在变量 n 中。
  3. 外层循环:遍历每一个加油站,尝试将其作为出发点。循环变量 start 从 0 到 n-1
  4. 初始化变量:
    • totalGas 用于记录从当前出发点出发的总汽油量。
    • totalCost 用于记录从当前出发点到下一个加油站的总消耗量。
    • canComplete 标记从当前起点出发是否可以完成一圈,初始设为 true
  5. 内层循环:从当前起点出发,遍历所有加油站。循环变量 i 从 0 到 n-1
    • 计算当前加油站的索引,使用取模运算以实现环形效果。
  6. 累加汽油量和消耗量:将当前加油站的汽油量加到 totalGas,将消耗量加到 totalCost
  7. 检查能否继续行驶:如果 totalGas 小于 totalCost,说明无法继续行驶,设置 canCompletefalse,并跳出内层循环。
  8. 判断能否完成一圈:如果 canComplete 仍为 true,说明可以从当前起点出发完成一圈,返回当前起点的索引。
  9. 返回结果:如果所有加油站都尝试过后仍然没有找到可以完成一圈的起点,返回 -1,表示无法完成。

思路三

var canCompleteCircuit = function (gas, cost) {const n = gas.length;const prefix = new Array(n + 1).fill(0);for (let i = 0; i < n; i++) {prefix[i + 1] = prefix[i] + gas[i] - cost[i];}let minPrefix = Infinity;let startIndex = 0;for (let i = 1; i <= n; i++) {if (prefix[i] < minPrefix) {minPrefix = prefix[i];startIndex = i;}}return prefix[n] >= 0 ? (startIndex % n) : -1;
};

讲解
这段代码实现了一个函数 canCompleteCircuit,用于判断在一个环形路线上,是否可以从某个加油站出发,顺利完成一圈而不耗尽汽油。

  1. 输入参数:函数接收两个数组 gascost,分别表示每个加油站的汽油量和从一个加油站到下一个加油站所需的汽油量。
  2. 获取加油站数量:通过 gas.length 获取加油站的数量,并存储在变量 n 中。
  3. 初始化前缀数组:创建一个长度为 n + 1 的数组 prefix,并用 0 填充。这个数组用于存储从起点到每个加油站的净汽油量(汽油量减去消耗量)。
  4. 计算前缀和:
    • 使用循环遍历每个加油站,更新 prefix 数组。prefix[i + 1] 存储到达第 i 个加油站后的净汽油量。
    • 计算公式为 prefix[i + 1] = prefix[i] + gas[i] - cost[i]
  5. 寻找最小前缀和:
    • 初始化 minPrefix 为正无穷,startIndex0
    • 再次遍历 prefix 数组,寻找最小的前缀和,并记录其索引 startIndex
  6. 判断是否可以完成一圈:
    • 如果 prefix[n](即完成一圈后的净汽油量)大于等于 0,说明可以完成一圈,返回 startIndex % n 作为起点。
    • 如果 prefix[n] 小于 0,返回 -1,表示无法完成。

思路四

var canCompleteCircuit = function (gas, cost) {const n = gas.length;let totalGas = 0;let totalCost = 0;let currentGas = 0;let startIndex = 0;for (let i = 0; i < n; i++) {totalGas += gas[i];totalCost += cost[i];currentGas += gas[i] - cost[i];if (currentGas < 0) {startIndex = i + 1;currentGas = 0;}}return totalGas >= totalCost ? startIndex : -1;
};

讲解
这段代码实现了一个函数 canCompleteCircuit,用于判断在一个环形路线上,是否可以从某个加油站出发,顺利完成一圈而不耗尽汽油。

  1. 输入参数
    • gas: 一个数组,表示每个加油站的汽油量。
    • cost: 一个数组,表示从一个加油站到下一个加油站所需的汽油量。
  2. 变量初始化
    • n: 加油站的数量。
    • totalGas: 用于累加所有加油站的汽油量。
    • totalCost: 用于累加所有加油站的消耗量。
    • currentGas: 用于跟踪当前剩余的汽油量。
    • startIndex: 记录可行的起始加油站索引。
  3. 遍历加油站
    • 使用 for 循环遍历每个加油站。
    • 在每次迭代中,更新 totalGastotalCost,并计算 currentGas(当前剩余汽油量)。
  4. 判断当前汽油量
    • 如果 currentGas 小于 0,说明从当前 startIndex 到第 i 个加油站无法完成,更新 startIndexi + 1,并重置 currentGas
  5. 返回结果
    • 在循环结束后,检查 totalGas 是否大于或等于 totalCost。如果是,返回 startIndex,否则返回 -1,表示无法完成一圈。

思路五

var canCompleteCircuit = function (gas, cost) {
const n = gas.length;const dp = new Array(n).fill(0);for (let i = 0; i < n; i++) {dp[i] = gas[i] - cost[i];}let totalGas = 0;let totalCost = 0;let currentGas = 0;let startIndex = 0;for (let i = 0; i < n; i++) {totalGas += gas[i];totalCost += cost[i];currentGas += dp[i];if (currentGas < 0) {startIndex = i + 1;currentGas = 0;}}return totalGas >= totalCost ? startIndex : -1;
};

讲解
这段代码通过使用一个额外的数组 dp 来存储每个加油站的净汽油量,并通过一次遍历(时间复杂度 O(n))有效地判断是否存在一个起点,使得从该起点出发能够完成一圈。这种方法同样比暴力法更高效,适用于大规模数据处理。

  1. 输入参数

    • gas: 一个数组,表示每个加油站的汽油量。
    • cost: 一个数组,表示从一个加油站到下一个加油站所需的汽油量。
  2. 变量初始化

    • n: 加油站的数量。
    • dp: 一个数组,用于存储每个加油站的净汽油量(gas[i] - cost[i])。
    • totalGas: 用于累加所有加油站的汽油量。
    • totalCost: 用于累加所有加油站的消耗量。
    • currentGas: 用于跟踪当前剩余的汽油量。
    • startIndex: 记录可行的起始加油站索引。
  3. 计算净汽油量

    • 使用 for 循环遍历每个加油站,将 dp[i] 设置为 gas[i] - cost[i],表示从第 i 个加油站出发的净汽油量。
  4. 遍历加油站

    • 再次使用 for 循环遍历每个加油站。
    • 在每次迭代中,更新 totalGastotalCost,并计算 currentGas(当前剩余汽油量)。
  5. 判断当前汽油量

    • 如果 currentGas 小于 0,说明从当前 startIndex 到第 i 个加油站无法完成,更新 startIndexi + 1,并重置 currentGas
  6. 返回结果

    • 在循环结束后,检查 totalGas 是否大于或等于 totalCost。如果是,返回 startIndex,否则返回 -1,表示无法完成一圈。
http://www.lryc.cn/news/468923.html

相关文章:

  • 强心剂!EEMD-MPE-KPCA-LSTM、EEMD-MPE-LSTM、EEMD-PE-LSTM故障识别、诊断
  • yarn的安装与使用以及与npm的区别(安装过程中可能会遇到的问题)
  • 大数据行业预测
  • 可能是NextJs(使用ssr、api route)打包成桌面端(nextron、electron、tauri)的最佳解决方式
  • 二百七十、Kettle——ClickHouse中增量导入清洗数据错误表
  • CentOS6升级OpenSSH9.2和OpenSSL3
  • 2024 年 MathorCup 数学应用挑战赛——大数据竞赛-赛道 A:台风的分类与预测
  • kotlin实现viewpager
  • RabbitMQ最新版本4.0.2在Windows下的安装及使用
  • 东方博宜1180 - 数字出现次数
  • LeetCode: 3274. 检查棋盘方格颜色是否相同
  • datax编译并测试
  • 2-133 基于matlab的粒子群算法PSO优化BP神经网络
  • 复盘秋招22场面试(四)形势重新评估与后续措施
  • 揭开C++ STL的神秘面纱之string:提升编程效率的秘密武器
  • 用人工智能,应该怎么掏钱?
  • 【Axure高保真原型】移动案例
  • Bytebase 3.0.0 - AI 助手全面升级
  • php基础:数据类型、常量、字符串
  • Discuz发布原创AI帖子内容生成:起尔 | AI原创帖子内容生成插件开发定制
  • el-table在某些条件下禁止选中
  • 深入探讨 HTTP 请求方法:GET、POST、PUT、DELETE 的实用指南
  • 深度学习:元学习(Meta-Learning)详解
  • uniapp展示本地pdf + 自定义标题
  • 国标GB28181设备管理软件EasyGBS国标GB28181-2016平台更换SQLite数据库的步骤
  • C++基础与实用技巧第三节:内存管理与性能优化
  • 【移动应用开发】界面设计(二)实现水果列表页面
  • 基于Multisim的四人智力竞赛抢答器设计与仿真
  • 前端学习---(4)js基础-2
  • 重生之“我打数据结构,真的假的?”--3.栈和队列(无习题)