演算法實踐——Twitter演算法面試題(積水問題)的線性時間解法

萬倉一黍發表於2013-11-08

問題描述:在下圖裡我們有不同高度的擋板。這個圖片由一個整數陣列所代表,陣列中每個數是牆的高度。下圖可以表示為陣列(2、5、1、2、3、4、7、2)。假如開始下雨了,那麼擋板之間的水坑能夠裝多少水(水足夠多)呢?

Wall-1

 

下圖是裝滿水的情況,一個藍色格子代表一個單位的水。下圖中一共裝了10個單位的水。

Wall-2

 

 

問題分析:

 

先看看下圖,判斷哪個單元格的水能留下來。下圖中的兩個單元格,一個紅色的單元格和一個綠色的單元格,哪個單元格的水是溜走了,哪個單元格的水能留下來?

Wall-3

很明顯的,上圖中的紅色單元格的水會流走,綠色單元格的水會被留下來。

那麼,仔細看看這兩個單元格的區別在哪兒

區別就是,紅色單元格只有右邊的擋板比它高(不低於它),而綠色單元格左右兩邊都有擋板比它高(左邊最高是5,右邊最高是7)

這也就很好的理解了,如果水要能留下來,必須左右兩邊的擋板都比它高才行。(很明顯的,不管哪一側的擋板比水低,水就會朝哪個方向流出去)

 

於是,我給每個擋板定義了3個屬性

V屬性:本擋板的高度

L屬性:本擋板左側擋板的最高高度

R屬性:本擋板右側擋板的最高高度

 

那麼,該擋板上方能積水的充要條件就是:L>V並且R>V

如果該擋板能積水,則積水量為:Min(L-V,R-V)

 

那麼總的積水量就是所有擋板的積水量總和

 

問題就變成,如何求出每塊擋板的L屬性和R屬性

用V、L、R三個陣列標示擋板組的三個屬性。一共有N塊擋板,陣列的下標從0到N-1。

V(i)表示第i塊擋板的高度、L(i)表示第i塊擋板的L屬性、R(i)表示第i塊擋板的R屬性

可知的是L(0)=0,R(N-1)=0;

 

不失一般性,考慮第i塊擋板的L屬性(i>0)。L(i-1)表示第i-1塊擋板的L屬性。那麼,可知

如果L(i-1)>V(i-1),則L(i)=L(i-1)

如果L(i-1)≤V(i-1),則L(i)=V(i-1)

綜上所述:L(i)=Max(L(i-1),V(i-1))(i>0)

同理可述:R(i)=Max(R(i+1),V(i+1))(i<N-1)

 

而由於L(0)=0,R(N-1)=0。則說明第0、N塊擋板(最左和最右的擋板)是不會積水的

因此,計算L和R的屬性以及計算積水量的下標從1開始到N-2即可

 

程式碼如下:

Public Class clsFillWater
    Public Shared Function FillWater2(ByVal ParamArray Nums() As Integer) As Integer
        Dim L(Nums.Length - 1) As Integer
        Dim R(Nums.Length - 1) As Integer
        Dim I As Integer
        Dim Result As Integer = 0

 

        If Nums.Length < 3 Then Return 0

 

        L(0) = 0
        R(Nums.Length - 1) = 0

        For I = 1 To Nums.Length - 2
            L(I) = Math.Max(L(I - 1), Nums(I - 1))
            R(Nums.Length - 1 - I) = Math.Max(R(Nums.Length - I), Nums(Nums.Length - I))
        Next

 

        For I = 1 To Nums.Length - 2
            If L(I) > Nums(I) AndAlso R(I) > Nums(I) Then
                Result += Math.Min(L(I), R(I)) - Nums(I)
            End If
        Next

        Return Result
    End Function
End Class

 

從程式碼看,該演算法的時間效率是O(N)的,是線性時間的。在文章 Twitter演算法面試題詳解(Java實現) 的評論中也有一個線性時間的演算法(效率相當,可能還優於本演算法),不過理解上不如這個簡單明瞭。

相關文章