[LeetCode] 用更簡單的方式分析與解決 #11 Container With Most Water

幾乎所有解答不管中英文都在講兩邊指針移動的演算法,難道就沒有其他解法? 我今天就來用一個很簡單的分析讓原本會超時 (TLE) 的暴力解法可以被 Accepted。

最大的容器有什麼特徵?

每個測資都一定有一個最大的容器,假設我們最後找到最大的容器好了:


我就想了一下,左邊什麼情況不會發生?

左邊有機會有更高的牆嗎?


不會,不然你早就找到更好的解答了:


因為不管怎樣左邊一定會凸出去一塊綠色的部分,那我們一開始找到的根本不是最佳解。

所以如果你找到最大的容器,左右邊絕對都不會有更高的牆:


可是演算法怎麼設計?

對啦,我們知道左右邊不會有更高的牆,可是我們還是要把解答找出來啊!演算法怎辦?

我們知道暴力解就是把每個可能性都 try 過,就會有兩個 for loop 一左一右試試看每種面積,我們就可以這樣寫:


#include <algorithm>

class Solution {
 public:
  int maxArea(std::vector<int> height) {
    int maxArea = 0;
    for (int left = 0; left < height.size(); left++) {
      const int heightLeft = height.at(left);
      for (int right = height.size() - 1; right > left; right--) {
        const int heightRight = height.at(right);
        const int width = right - left;
        const int minHeight = std::min(heightLeft, heightRight);
        const int area = minHeight * width;
        maxArea = std::max(area, maxArea);
      }
    }
    return maxArea;
  }
};


只是我第二層的 index 是從右往左,但只是和一般的作法方向相反而已,每種可能性都還是會試過,所以沒差。

這丟上去當然會超時:


但還沒結束。我們就來想像一下兩個 indexes 的移動過程,如果移動到一半找到目前最大的容器,繼續碰到一根更短的牆他就不可能有更好的解:


根據我們一開始的結論,不管更短的牆找到多大的容器,我們現在的容器一定更大。所以碰到比目前更短的牆我們根本就不用理他。

找個 LeetCode 問題描述中的圖片當例子,從左往右就會把這些牆都略過:


從右往左的話:


這樣子我們只要把會 TLE 的暴力解稍微改一下,如果更短就跳過:


#include <algorithm>

class Solution {
 public:
  int maxArea(std::vector<int> &height) {
    int maxArea = 0;
    int prevHeightLeft = 0;
    for (int left = 0; left < height.size(); left++) {
      const int heightLeft = height.at(left);
      if (prevHeightLeft >= heightLeft) continue;

      int prevHeightRight = 0;
      for (int right = height.size() - 1; right > left; right--) {
        const int heightRight = height.at(right);
        if (heightRight <= prevHeightRight) continue;

        const int width = right - left;
        const int minHeight = std::min(heightLeft, heightRight);
        const int area = minHeight * width;
        maxArea = std::max(area, maxArea);
        prevHeightRight = heightRight;
      }
      prevHeightLeft = heightLeft;
    }
    return maxArea;
  }
};


至少可以加快一些時間:


就解完啦!

一樣是 O(n^2) 怎麼會比較快? 因為省略掉一些牆之後 n 的數量就下降了。

結語

雖然時間慢到爆炸,但至少我們有動腦分析與設計,而不是就把程式碼和演算法直接丟上來給人參考。

和任何事一樣,寫程式最好玩的是思考的過程,這大概也是面試時面試官會想要看的東西。

下一篇我用圖片的方式來解釋最常見的雙指針作法為何不會有問題。

GitHub

有興趣的話我把 Code 和投影片我都放在 GitHub 上了。

留言

此網誌的熱門文章

[試算表] 追蹤台股 Google Spreadsheet (未實現損益/已實現損益)

[Side Project] 互動式教學神經網路反向傳播 Interactive Computational Graph

[插件] 在 Chrome 網頁做區分大小寫的搜尋