<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          手把手搞懂接雨水問題的多種解法

          共 867字,需瀏覽 2分鐘

           ·

          2020-10-29 00:46

          讀完本文,可以去力扣解決如下題目:

          42.接雨水(Hard

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

          就是用一個數(shù)組表示一個條形圖,問你這個條形圖最多能接多少水,函數(shù)簽名如下:

          int?trap(int[]?height);

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

          一、暴力解法

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

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

          能裝 2 格水,因為height[i]的高度為 0,而這里最多能盛 2 格水,2-0=2。

          為什么位置i最多能盛 2 格水呢?因為,位置i能達(dá)到的水柱高度和其左邊的最高柱子、右邊的最高柱子有關(guān),我們分別稱這兩個柱子高度為l_maxr_max;位置 i 最大的水柱高度就是min(l_max, r_max)。

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

          water[i]?=?min(
          ???????????????#?左邊最高的柱子
          ???????????????max(height[0..i]),??
          ???????????????#?右邊最高的柱子
          ???????????????max(height[i..end])?
          ????????????)?-?height[i]
          這就是本問題的核心思路,我們可以簡單寫一個暴力算法:
          int?trap(vector<int>&?height)?{
          ????int?n?=?height.size();
          ????int?res?=?0;
          ????for?(int?i?=?1;?i?1;?i++)?{
          ????????int?l_max?=?0,?r_max?=?0;
          ????????//?找右邊最高的柱子
          ????????for?(int?j?=?i;?j?????????????r_max?=?max(r_max,?height[j]);
          ????????//?找左邊最高的柱子
          ????????for?(int?j?=?i;?j?>=?0;?j--)
          ????????????l_max?=?max(l_max,?height[j]);
          ????????//?如果自己就是最高的話,
          ????????//?l_max?==?r_max?==?height[i]
          ????????res?+=?min(l_max,?r_max)?-?height[i];
          ????}
          ????return?res;
          }

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

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

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

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

          int?trap(vector<int>&?height)?{
          ????if?(height.empty())?return?0;
          ????int?n?=?height.size();
          ????int?res?=?0;
          ????//?數(shù)組充當(dāng)備忘錄
          ????vector<int>?l_max(n),?r_max(n);
          ????//?初始化?base?case
          ????l_max[0]?=?height[0];
          ????r_max[n?-?1]?=?height[n?-?1];
          ????//?從左向右計算?l_max
          ????for?(int?i?=?1;?i?????????l_max[i]?=?max(height[i],?l_max[i?-?1]);
          ????//?從右向左計算?r_max
          ????for?(int?i?=?n?-?2;?i?>=?0;?i--)?
          ????????r_max[i]?=?max(height[i],?r_max[i?+?1]);
          ????//?計算答案
          ????for?(int?i?=?1;?i?1;?i++)?
          ????????res?+=?min(l_max[i],?r_max[i])?-?height[i];
          ????return?res;
          }

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

          三、雙指針解法

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

          首先,看一部分代碼:

          int?trap(vector<int>&?height)?{
          ????int?n?=?height.size();
          ????int?left?=?0,?right?=?n?-?1;

          ????int?l_max?=?height[0];
          ????int?r_max?=?height[n?-?1];

          ????while?(left?<=?right)?{
          ????????l_max?=?max(l_max,?height[left]);
          ????????r_max?=?max(r_max,?height[right]);
          ????????left++;?right--;
          ????}
          }

          對于這部分代碼,請問l_maxr_max分別表示什么意義呢?

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

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

          int?trap(vector<int>&?height)?{
          ????if?(height.empty())?return?0;
          ????int?n?=?height.size();
          ????int?left?=?0,?right?=?n?-?1;
          ????int?res?=?0;

          ????int?l_max?=?height[0];
          ????int?r_max?=?height[n?-?1];

          ????while?(left?<=?right)?{
          ????????l_max?=?max(l_max,?height[left]);
          ????????r_max?=?max(r_max,?height[right]);

          ????????//?res?+=?min(l_max,?r_max)?-?height[i]
          ????????if?(l_max?????????????res?+=?l_max?-?height[left];
          ????????????left++;?
          ????????}?else?{
          ????????????res?+=?r_max?-?height[right];
          ????????????right--;
          ????????}
          ????}
          ????return?res;
          }

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

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

          res?+=?min(l_max[i],?r_max[i])?-?height[i];

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

          if?(l_max?????res?+=?l_max?-?height[left];
          ????left++;?
          }?

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

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

          這樣,接雨水問題就解決了,學(xué)會了嗎?三連安排!



          瀏覽 35
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  男女在线观看视频 | 我要看操逼 | 18日本XXXXXXXXX96-百度 | 操逼在线看 | 国产欧美在线免费观看 |