從更本質(zhì)的角度去看「加油站」問題

題目描述

這是 LeetCode 上的 「134. 加油站」 竿裂,難度為 「中等」玉吁。

在一條環(huán)路上有?N?個加油站,其中第?i?個加油站有汽油?gas[i]?升腻异。

你有一輛油箱容量無限的的汽車进副,從第 i 個加油站開往第 i+1?個加油站需要消耗汽油?cost[i]?升。你從其中的一個加油站出發(fā)悔常,開始時油箱為空影斑。

如果你可以繞環(huán)路行駛一周,則返回出發(fā)時加油站的編號这嚣,否則返回 -1鸥昏。

說明:

  • 如果題目有解,該答案即為唯一答案姐帚。
  • 輸入數(shù)組均為非空數(shù)組,且長度相同障涯。
  • 輸入數(shù)組中的元素均為非負數(shù)罐旗。

示例?1:

輸入:?
gas??=?[1,2,3,4,5]
cost?=?[3,4,5,1,2]

輸出:?3

解釋:
從 3 號加油站(索引為 3 處)出發(fā),可獲得 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 號加油站出發(fā)畅厢,因為沒有足夠的汽油可以讓你行駛到下一個加油站。
我們從 2 號加油站出發(fā)氮昧,可以獲得 4 升汽油框杜。?此時油箱有?=?0?+ 4 = 4 升汽油
開往?0?號加油站浦楣,此時油箱有?4?-?3?+?2?=?3?升汽油
開往?1?號加油站,此時油箱有?3?-?3?+?3?=?3?升汽油
你無法返回 2 號加油站咪辱,因為返程需要消耗 4 升汽油振劳,但是你的油箱只有 3 升汽油。
因此油狂,無論怎樣历恐,你都不可能繞環(huán)路行駛一周。

基本分析/樸素解法

這是一道比較經(jīng)典的題目选调。

估計在 LeetCode 也是有一段時間了夹供,所以連數(shù)據(jù)范圍都沒有。

我這里直接規(guī)定一下數(shù)據(jù)范圍為 仁堪,這意味著我們不能使用 做法了哮洽。

但樸素做法往往是優(yōu)化的出發(fā)點,所以我們還是先分析一下弦聂,樸素的做法是怎么樣的:

  • 題目要求「合法起點」的下標鸟辅,因此我們可以枚舉所有的「起點」

  • 然后按照「油量 & 成本」模擬一遍,看是否能走完一圈

共有 個「起點」莺葫,檢查某個「起點」合法性的復雜度是 的匪凉。因此整體復雜度為 的。

代碼:

class?Solution?{
????public?int?canCompleteCircuit(int[]?gas,?int[]?cost)?{
????????int?n?=?gas.length;
????????for?(int?start?=?0;?start?<?n;?start++)?{
????????????//?直接跳過第一步都不滿足的起點
????????????if?(gas[start]?<?cost[start])?continue;
????????????//?剩余油量
????????????int?cur?=?gas[start]?-?cost[start];
????????????//?所在位置
????????????int?idx?=?(start?+?1)?%?n;
????????????while?(idx?!=?start)?{
????????????????cur?+=?gas[idx]?-?cost[idx];
????????????????//?如果剩余油量為負數(shù)捺檬,說明無法離開當前位置再层,走到下一位置
????????????????if?(cur?<?0)?break;
????????????????idx?=?(idx?+?1)?%?n;
????????????}
????????????if?(idx?==?start)?return?start;
????????}
????????return?-1;
????}
}
  • 時間復雜度:
  • 空間復雜度:

PS. 在 LeetCode 上提交了一下,是可以過的 ??

KMP/DFA

不考慮我們跳過那些第一步就不滿足的起點的話堡纬,將上述解法中的兩個主要邏輯“單獨”拎出來看聂受,都是無法優(yōu)化的:

  • 在沒做任何操作之前,我們無法知道哪些起點是不合法的
  • 沒有比 更低的復雜度可以驗證一個起點的合法性

因此只能使用 解法烤镐?

并不是蛋济。單獨看無法優(yōu)化,合在一起則可以:隨著某個起點「合法性驗證」失敗炮叶,我們可以否決掉一些「必然不合法」的方案碗旅。

什么意思呢?

在樸素解法中镜悉,當我們驗證了某個起點 失斔畋佟(無法走完一圈)之后,我們接下來會去嘗試驗證起點 积瞒。

這時候驗證 失敗過程中產(chǎn)生的“信息”川尖,其實并沒有貢獻到之后的算法中。

事實上,起點 驗證失敗叮喳,其實是意味存在某個位置 使得「當前油量」為負數(shù)被芳。而這個位置 就是驗證起點 這過程中產(chǎn)生的“信息”。

它可以產(chǎn)生的價值是:在位置 和位置 之間的所有位置馍悟,都不可能是一個合法起點畔濒。也就是說,隨著起點 驗證失敗锣咒,我們可以否決掉方案不僅僅是位置 侵状,而是 這些位置。

我們可以證明為什么會有這樣的性質(zhì):

首先毅整,可以明確的是:因為 gas 數(shù)組和 cost 數(shù)組是給定的趣兄,因此每個位置的「凈消耗」是固定的,與從哪個「起點」出發(fā)無關(guān)悼嫉。

凈消耗是指該位置提供的「油量」和「到達下一位置的油耗」艇潭,也就是 gas[i]cast[i] 之間的差值。

證明過程如圖:

因此戏蔑,當有了這個優(yōu)化之后蹋凝,我們每個位置其實只會被遍歷常數(shù)次:當位置 作為「起點」驗證失敗后,驗證過程中遍歷過的位置就不需要再作為「起點」被驗證了总棵。

代碼:

class?Solution?{
????public?int?canCompleteCircuit(int[]?gas,?int[]?cost)?{
????????int?n?=?gas.length;
????????for?(int?start?=?0;?start?<?n;?)?{
????????????if?(gas[start]?<?cost[start])?{
????????????????start++;
????????????}?else?{
????????????????int?cur?=?gas[start]?-?cost[start];
????????????????int?idx?=?start?+?1;
????????????????while?(idx?%?n?!=?start)?{
????????????????????cur?+=?gas[idx?%?n]?-?cost[idx?%?n];
????????????????????if?(cur?<?0)?break;
????????????????????idx++;
????????????????}
????????????????if?(idx?%?n?==?start)?return?start;
????????????????else?start?=?idx;
????????????}
????????}
????????return?-1;
????}
}
  • 時間復雜度:
  • 空間復雜度:

總結(jié)

看到這里鳍寂,你可以已經(jīng)理解了 解法是怎么來的了。

但可能還不清楚這個優(yōu)化思路的本質(zhì)是什么情龄,其實這個優(yōu)化的本質(zhì)與 KMP 為什么可以做到線性匹配如出一轍迄汛。

本質(zhì)都是利用某次匹配(驗證)過程產(chǎn)生的“信息”,來加速之后的匹配(驗證)過程(否決掉一些必然合法的方案)骤视。

在我寫過的 KMP 題解里隔心,有這么一句原話:

?

當我們的原串指針從 i 位置后移到 j 位置,不僅僅代表著「原串」下標范圍為 的字符與「匹配串」匹配或者不匹配尚胞,更是在否決那些以「原串」下標范圍為 為「匹配發(fā)起點」的子集。

?

所以帜慢,從更本質(zhì)的角度出發(fā)笼裳,這道題其實是一道「KMP」思想應用題,或者說廣泛性的「DFA」題粱玲。

其他

在寫「總結(jié)」部分的時候躬柬,我還特意去看了一下題解區(qū),沒有人提到過「KMP」和「DFA」抽减,幾乎所有題解都停留在題目標簽「貪心算法」的角度去思考允青。

這是不對的,題目標簽的擬定很大程度取決于「寫這個標簽的人的水平」和「ta 當時看這道題的思考角度」卵沉,是一個主觀的結(jié)果颠锉。

學習算法和數(shù)據(jù)結(jié)構(gòu)法牲,應該是去理解每個算法和數(shù)據(jù)結(jié)構(gòu)的“某個操作”為什么能夠帶來優(yōu)化效果,并將該優(yōu)化效果的“底層思想”挖掘出來琼掠,應用到我們沒見過的問題中拒垃,這才是真正的“學習”。

最后

這是我們「刷穿 LeetCode」系列文章的第 No.134 篇瓷蛙,系列開始于 2021/01/01悼瓮,截止于起始日 LeetCode 上共有 1916 道題目,部分是有鎖題艰猬,我們將先將所有不帶鎖的題目刷完横堡。

在這個系列文章里面,除了講解解題思路以外冠桃,還會盡可能給出最為簡潔的代碼命贴。如果涉及通解還會相應的代碼模板。

為了方便各位同學能夠電腦上進行調(diào)試和提交代碼腊满,我建立了相關(guān)的倉庫:https://github.com/SharingSource/LogicStack-LeetCode 套么。

在倉庫地址里,你可以看到系列文章的題解鏈接碳蛋、系列文章的相應代碼胚泌、LeetCode 原題鏈接和其他優(yōu)選題解。

本文使用 文章同步助手 同步

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末肃弟,一起剝皮案震驚了整個濱河市玷室,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌笤受,老刑警劉巖穷缤,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異箩兽,居然都是意外死亡津肛,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門汗贫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來身坐,“玉大人,你說我怎么就攤上這事落包〔可撸” “怎么了?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵咐蝇,是天一觀的道長涯鲁。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么抹腿? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任岛请,我火速辦了婚禮,結(jié)果婚禮上幢踏,老公的妹妹穿的比我還像新娘髓需。我一直安慰自己,他們只是感情好房蝉,可當我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布僚匆。 她就那樣靜靜地躺著,像睡著了一般搭幻。 火紅的嫁衣襯著肌膚如雪咧擂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天檀蹋,我揣著相機與錄音松申,去河邊找鬼。 笑死俯逾,一個胖子當著我的面吹牛贸桶,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播桌肴,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼皇筛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了坠七?” 一聲冷哼從身側(cè)響起水醋,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎彪置,沒想到半個月后拄踪,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡拳魁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年惶桐,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片潘懊。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡耀盗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出卦尊,到底是詐尸還是另有隱情,我是刑警寧澤舌厨,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布岂却,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏躏哩。R本人自食惡果不足惜署浩,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望扫尺。 院中可真熱鬧筋栋,春花似錦、人聲如沸正驻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽姑曙。三九已至襟交,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間伤靠,已是汗流浹背捣域。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宴合,地道東北人焕梅。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像卦洽,于是被迫代替她去往敵國和親贞言。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容