0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

接雨水問題的三種解法:暴力/備忘錄/雙指針

算法與數(shù)據(jù)結(jié)構(gòu) ? 來(lái)源:labuladong ? 作者:labuladong ? 2022-05-17 13:24 ? 次閱讀
讀完本文,可以去力扣解決如下題目:

42. 接雨水(困難

接雨水這道題目挺有意思,在面試題中出現(xiàn)頻率還挺高的,本文就來(lái)步步優(yōu)化,講解一下這道題。

先看一下題目:

5ce42ff0-d594-11ec-bce3-dac502259ad0.png

就是用一個(gè)數(shù)組表示一個(gè)條形圖,問你這個(gè)條形圖最多能接多少水。

inttrap(int[]height);

下面就來(lái)由淺入深介紹暴力解法 -> 備忘錄解法 -> 雙指針解法,在 O(N) 時(shí)間 O(1) 空間內(nèi)解決這個(gè)問題。

一、核心思路

所以對(duì)于這種問題,我們不要想整體,而應(yīng)該去想局部;就像之前的文章寫的動(dòng)態(tài)規(guī)劃問題處理字符串問題,不要考慮如何處理整個(gè)字符串,而是去思考應(yīng)該如何處理每一個(gè)字符。

這么一想,可以發(fā)現(xiàn)這道題的思路其實(shí)很簡(jiǎn)單。具體來(lái)說(shuō),僅僅對(duì)于位置i,能裝下多少水呢?

5cf77416-d594-11ec-bce3-dac502259ad0.jpg

能裝 2 格水,因?yàn)?code style="margin-right:2px;margin-left:2px;padding:2px 4px;font-size:inherit;line-height:inherit;color:rgb(233,105,0);background:rgb(248,248,248);">height[i]的高度為 0,而這里最多能盛 2 格水,2-0=2。

為什么位置i最多能盛 2 格水呢?因?yàn)椋恢?code style="margin-right:2px;margin-left:2px;padding:2px 4px;font-size:inherit;line-height:inherit;color:rgb(233,105,0);background:rgb(248,248,248);">i能達(dá)到的水柱高度和其左邊的最高柱子、右邊的最高柱子有關(guān),我們分別稱這兩個(gè)柱子高度為l_maxr_max;位置 i 最大的水柱高度就是min(l_max, r_max)。

更進(jìn)一步,對(duì)于位置i,能夠裝的水為:

water[i]=min(
#左邊最高的柱子
max(height[0..i]),
#右邊最高的柱子
max(height[i..end])
)-height[i]
5d0bebb2-d594-11ec-bce3-dac502259ad0.jpg5d395b42-d594-11ec-bce3-dac502259ad0.jpg

這就是本問題的核心思路,我們可以簡(jiǎn)單寫一個(gè)暴力算法

inttrap(int[]height){
intn=height.length;
intres=0;
for(inti=1;i1;i++){
intl_max=0,r_max=0;
//找右邊最高的柱子
for(intj=i;j//找左邊最高的柱子
for(intj=i;j>=0;j--)
l_max=Math.max(l_max,height[j]);
//如果自己就是最高的話,
//l_max==r_max==height[i]
res+=Math.min(l_max,r_max)-height[i];
}
returnres;
}

有之前的思路,這個(gè)解法應(yīng)該是很直接粗暴的,時(shí)間復(fù)雜度 O(N^2),空間復(fù)雜度 O(1)。但是很明顯這種計(jì)算r_maxl_max的方式非常笨拙,一般的優(yōu)化方法就是備忘錄。

二、備忘錄優(yōu)化

之前的暴力解法,不是在每個(gè)位置i都要計(jì)算r_maxl_max嗎?我們直接把結(jié)果都提前計(jì)算出來(lái),別傻不拉幾的每次都遍歷,這時(shí)間復(fù)雜度不就降下來(lái)了嘛。

我們開兩個(gè)數(shù)組r_maxl_max充當(dāng)備忘錄,l_max[i]表示位置i左邊最高的柱子高度,r_max[i]表示位置i右邊最高的柱子高度。預(yù)先把這兩個(gè)數(shù)組計(jì)算好,避免重復(fù)計(jì)算:

inttrap(int[]height){
if(height.length==0){
return0;
}
intn=height.length;
intres=0;
//數(shù)組充當(dāng)備忘錄
int[]l_max=newint[n];
int[]r_max=newint[n];
//初始化basecase
l_max[0]=height[0];
r_max[n-1]=height[n-1];
//從左向右計(jì)算l_max
for(inti=1;i1]);
//從右向左計(jì)算r_max
for(inti=n-2;i>=0;i--)
r_max[i]=Math.max(height[i],r_max[i+1]);
//計(jì)算答案
for(inti=1;i1;i++)
res+=Math.min(l_max[i],r_max[i])-height[i];
returnres;
}

這個(gè)優(yōu)化其實(shí)和暴力解法思路差不多,就是避免了重復(fù)計(jì)算,把時(shí)間復(fù)雜度降低為 O(N),已經(jīng)是最優(yōu)了,但是空間復(fù)雜度是 O(N)。下面來(lái)看一個(gè)精妙一些的解法,能夠把空間復(fù)雜度降低到 O(1)。

三、雙指針解法

這種解法的思路是完全相同的,但在實(shí)現(xiàn)手法上非常巧妙,我們這次也不要用備忘錄提前計(jì)算了,而是用雙指針邊走邊算,節(jié)省下空間復(fù)雜度。

首先,看一部分代碼:

inttrap(int[]height){
intleft=0,right=height.length-1;
intl_max=0,r_max=0;

while(left//此時(shí) l_max 和 r_max 分別表示什么?
left++;right--;
}
}

對(duì)于這部分代碼,請(qǐng)問l_maxr_max分別表示什么意義呢?

很容易理解,l_maxheight[0..left]中最高柱子的高度,r_maxheight[right..end]的最高柱子的高度

明白了這一點(diǎn),直接看解法:

inttrap(int[]height){
intleft=0,right=height.length-1;
intl_max=0,r_max=0;

intres=0;
while(left//res+=min(l_max,r_max)-height[i]
if(l_maxelse{
res+=r_max-height[right];
right--;
}
}
returnres;
}

你看,其中的核心思想和之前一模一樣,換湯不換藥。但是細(xì)心的讀者可能會(huì)發(fā)現(xiàn)次解法還是有點(diǎn)細(xì)節(jié)差異:

之前的備忘錄解法,l_max[i]r_max[i]分別代表height[0..i]height[i..end]的最高柱子高度。

res+=Math.min(l_max[i],r_max[i])-height[i];
5d4a46d2-d594-11ec-bce3-dac502259ad0.jpg

但是雙指針解法中,l_maxr_max代表的是height[0..left]height[right..end]的最高柱子高度。比如這段代碼:

if(l_max
5d5a2b6a-d594-11ec-bce3-dac502259ad0.jpg

此時(shí)的l_maxleft指針左邊的最高柱子,但是r_max并不一定是left指針右邊最高的柱子,這真的可以得到正確答案嗎?

其實(shí)這個(gè)問題要這么思考,我們只在乎min(l_max, r_max)。對(duì)于上圖的情況,我們已經(jīng)知道l_max < r_max了,至于這個(gè)r_max是不是右邊最大的,不重要。重要的是height[i]能夠裝的水只和較低的l_max之差有關(guān)

5d6901ee-d594-11ec-bce3-dac502259ad0.jpg

這樣,接雨水問題就解決了。

擴(kuò)展延伸

下面我們看一道和接雨水問題非常類似的題目,力扣第 11 題「盛最多水的容器」:

5d9be2c6-d594-11ec-bce3-dac502259ad0.png

函數(shù)簽名如下:

intmaxArea(int[]height);

這題和接雨水問題很類似,可以完全套用前文的思路,而且還更簡(jiǎn)單。兩道題的區(qū)別在于:

接雨水問題給出的類似一幅直方圖,每個(gè)橫坐標(biāo)都有寬度,而本題給出的每個(gè)橫坐標(biāo)是一條豎線,沒有寬度。

我們前文討論了半天l_maxr_max,實(shí)際上都是為了計(jì)算height[i]能夠裝多少水;而本題中height[i]沒有了寬度,那自然就好辦多了。

舉個(gè)例子,如果在接雨水問題中,你知道了height[left]height[right]的高度,你能算出leftright之間能夠盛下多少水嗎?

不能,因?yàn)槟悴恢?code style="margin-right:2px;margin-left:2px;padding:2px 4px;font-size:inherit;line-height:inherit;color:rgb(233,105,0);background:rgb(248,248,248);">leftright之間每個(gè)柱子具體能盛多少水,你得通過(guò)每個(gè)柱子的l_maxr_max來(lái)計(jì)算才行。

反過(guò)來(lái),就本題而言,你知道了height[left]height[right]的高度,能算出leftright之間能夠盛下多少水嗎?

可以,因?yàn)楸绢}中豎線沒有寬度,所以leftright之間能夠盛的水就是:

min(height[left],height[right])*(right-left)

類似接雨水問題,高度是由height[left]height[right]較小的值決定的。

解決這道題的思路依然是雙指針技巧:

leftright兩個(gè)指針從兩端向中心收縮,一邊收縮一邊計(jì)算[left, right]之間的矩形面積,取最大的面積值即是答案。

先直接看解法代碼吧:

intmaxArea(int[]height){
intleft=0,right=height.length-1;
intres=0;
while(left//[left,right]之間的矩形面積
intcur_area=Math.min(height[left],height[right])*(right-left);
res=Math.max(res,cur_area);
//雙指針技巧,移動(dòng)較低的一邊
if(height[left]else{
right--;
}
}
returnres;
}

代碼和接雨水問題大致相同,不過(guò)肯定有讀者會(huì)問,下面這段 if 語(yǔ)句為什么要移動(dòng)較低的一邊:

//雙指針技巧,移動(dòng)較低的一邊
if(height[left]else{
right--;
}

其實(shí)也好理解,因?yàn)榫匦蔚母叨仁怯?code style="margin-right:2px;margin-left:2px;padding:2px 4px;font-size:inherit;line-height:inherit;color:rgb(233,105,0);background:rgb(248,248,248);">min(height[left], height[right])即較低的一邊決定的

你如果移動(dòng)較低的那一邊,那條邊可能會(huì)變高,使得矩形的高度變大,進(jìn)而就「有可能」使得矩形的面積變大;相反,如果你去移動(dòng)較高的那一邊,矩形的高度是無(wú)論如何都不會(huì)變大的,所以不可能使矩形的面積變得更大。

至此,這道題也解決了。

原文標(biāo)題:詳解一道高頻面試題:接雨水

文章出處:【微信公眾號(hào):算法與數(shù)據(jù)結(jié)構(gòu)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4722

    瀏覽量

    68236
  • 數(shù)組
    +關(guān)注

    關(guān)注

    1

    文章

    412

    瀏覽量

    25881

原文標(biāo)題:詳解一道高頻面試題:接雨水

文章出處:【微信號(hào):TheAlgorithm,微信公眾號(hào):算法與數(shù)據(jù)結(jié)構(gòu)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    蘋果iOS 18.2將推項(xiàng)備忘錄AI功能,提升創(chuàng)作效率

    11月6日,據(jù)外媒報(bào)道,蘋果公司正籌備推出第二波Apple Intelligence(蘋果智能)功能,并計(jì)劃在下個(gè)月發(fā)布的iOS 18.2更新中,為備忘錄應(yīng)用帶來(lái)項(xiàng)關(guān)鍵的人工智能改進(jìn),旨在提升用戶的創(chuàng)作效率和日常記錄體驗(yàn)。
    的頭像 發(fā)表于 11-06 14:58 ?305次閱讀

    新紫光集團(tuán)與埃及簽署合作備忘錄

    近日,新紫光集團(tuán)在北京與埃及信息技術(shù)產(chǎn)業(yè)發(fā)展局(ITIDA)、應(yīng)用創(chuàng)新中心(AIC)及埃及電信公司(Telecom Egypt)等重量級(jí)部門及機(jī)構(gòu)成功簽署了一項(xiàng)全面合作備忘錄,標(biāo)志著雙方在數(shù)智化轉(zhuǎn)型與產(chǎn)業(yè)升級(jí)領(lǐng)域的合作邁入嶄新階段。
    的頭像 發(fā)表于 09-09 17:26 ?993次閱讀

    英飛凌與韓國(guó)造船海洋簽署合作備忘錄

    近日,全球功率半導(dǎo)體巨頭英飛凌科技與韓國(guó)造船海洋(HD KSOE)共同簽署了一份諒解備忘錄(MoU)。此次合作旨在推動(dòng)低碳節(jié)能,通過(guò)功率半導(dǎo)體技術(shù)聯(lián)合開發(fā)新型船用發(fā)動(dòng)機(jī)和推動(dòng)船舶機(jī)械電氣化。
    的頭像 發(fā)表于 05-07 15:15 ?483次閱讀

    HarmonyOS開發(fā)實(shí)例:【手機(jī)備忘錄

    基于用戶首選項(xiàng),實(shí)現(xiàn)了備忘錄新增、更新、刪除以及查找等功能。
    的頭像 發(fā)表于 04-18 21:40 ?717次閱讀
    HarmonyOS開發(fā)實(shí)例:【手機(jī)<b class='flag-5'>備忘錄</b>】

    蘋果iOS 18將支持語(yǔ)音備忘錄及數(shù)學(xué)符號(hào)顯示

    首先是語(yǔ)音備忘錄功能。據(jù)悉,蘋果有意在iOS 18系統(tǒng)中加入此項(xiàng)功能,使iPhone用戶能夠便捷地錄制音頻文件,并將其直接嵌入至備忘錄之中。
    的頭像 發(fā)表于 04-18 11:14 ?479次閱讀

    容百科技宣布與SK On簽訂《合作備忘錄

    本周,容百科技宣布與SK On簽訂《合作備忘錄》,雙方將圍繞元和磷酸錳鐵鋰正極開展深度合作。
    的頭像 發(fā)表于 03-29 09:56 ?410次閱讀

    英特爾攜手Arm簽署合作備忘錄

    近日,英特爾與Arm兩大科技巨頭簽署了一份諒解備忘錄,宣布在“新興企業(yè)支持計(jì)劃”上展開深度合作。該計(jì)劃源于今年2月Intel Foundry Direct Connect活動(dòng)中的公布,進(jìn)一步夯實(shí)了英特爾與Arm去年四月達(dá)成的戰(zhàn)略合作框架。
    的頭像 發(fā)表于 03-26 09:28 ?496次閱讀

    華為與華訊網(wǎng)絡(luò)簽署合作備忘錄,共同推動(dòng)客戶數(shù)智化升級(jí)進(jìn)程

    以“因聚而生 數(shù)智有為”為主題的“華為中國(guó)合作伙伴大會(huì)2024”期間,華為與上海華訊網(wǎng)絡(luò)系統(tǒng)有限公司(以下簡(jiǎn)稱“華訊網(wǎng)絡(luò)”)簽署了合作備忘錄。
    的頭像 發(fā)表于 03-17 17:25 ?1234次閱讀

    霍尼韋爾與南方泵業(yè)簽署戰(zhàn)略合作備忘錄

    2024年3月7日,霍尼韋爾智能工業(yè)科技集團(tuán)與南方泵業(yè)股份有限公司(以下簡(jiǎn)稱“南方泵業(yè)”)簽署戰(zhàn)略合作備忘錄。
    的頭像 發(fā)表于 03-13 17:21 ?623次閱讀

    小馬智行宣布與盧森堡大公國(guó)政府簽署諒解備忘錄

    近日,小馬智行宣布與盧森堡大公國(guó)政府簽署諒解備忘錄,促進(jìn)自動(dòng)駕駛汽車及技術(shù)在盧森堡的發(fā)展。
    的頭像 發(fā)表于 03-12 12:20 ?412次閱讀

    軟通動(dòng)力與華為簽訂中東中亞合作啟動(dòng)備忘錄

    3月4日,軟通動(dòng)力與華為在沙特阿拉伯首都利雅得,簽訂中東中亞合作啟動(dòng)備忘錄,未來(lái)雙方將以互利共贏為合作原則,共同深化中東中亞生態(tài)和伙伴業(yè)務(wù)合作。
    的頭像 發(fā)表于 03-06 09:56 ?437次閱讀

    利亞德與沙特簽署合作備忘錄

    利亞德集團(tuán)正積極拓展其在沙特市場(chǎng)的業(yè)務(wù)布局。近期,利亞德與中國(guó)-沙特阿拉伯投資峰會(huì)、沙特投資部以及沙特工程控股集團(tuán)簽署了合作備忘錄,計(jì)劃共同在沙特利雅得建立合資公司,將中國(guó)的LED顯示先進(jìn)技術(shù)及高端制造產(chǎn)線引入當(dāng)?shù)亍?/div>
    的頭像 發(fā)表于 02-02 15:47 ?942次閱讀

    芯馳科技與上汽大眾簽訂合作備忘錄

    12月6日,上海市汽車芯片產(chǎn)業(yè)創(chuàng)新發(fā)展工作推進(jìn)會(huì)順利舉行。會(huì)上,芯馳科技與上汽大眾汽車有限公司(以下簡(jiǎn)稱上汽大眾)簽訂合作備忘錄,雙方將共同推進(jìn)智能汽車電子技術(shù)的研發(fā)與應(yīng)用,促進(jìn)智能汽車領(lǐng)域的艙泊混合域控制器的技術(shù)創(chuàng)新與市場(chǎng)拓展。
    的頭像 發(fā)表于 12-07 10:54 ?1257次閱讀

    實(shí)踐GoF的23設(shè)計(jì)模式:備忘錄模式

    相對(duì)于代理模式、工廠模式等設(shè)計(jì)模式,備忘錄模式(Memento)在我們?nèi)粘i_發(fā)中出鏡率并不高,除了應(yīng)用場(chǎng)景的限制之外,另一個(gè)原因,可能是備忘錄模式
    的頭像 發(fā)表于 11-25 09:05 ?506次閱讀
    實(shí)踐GoF的23<b class='flag-5'>種</b>設(shè)計(jì)模式:<b class='flag-5'>備忘錄</b>模式

    寧德時(shí)代考慮赴港上市,宣布與Stellantis簽署戰(zhàn)略諒解備忘錄

    近日,寧德時(shí)代與Stellantis集團(tuán)發(fā)布了戰(zhàn)略諒解備忘錄,寧德時(shí)代將向Stellantis集團(tuán)供應(yīng)磷酸鐵鋰電池,并在兩個(gè)戰(zhàn)略方面展開長(zhǎng)期合作。
    的頭像 發(fā)表于 11-23 17:26 ?704次閱讀