第一次寫博客,我這種水平的大學生程序猿爲什麼會想到要寫博客這種東西呢,而且還要周更呢?沒錯,這是我們老師佈置的作業。如果覺得我的博客寫得有何不妥,請在下方留言,每週我會抽取幸運觀衆進行回覆,謝謝大家。
進入正題,這是我們算法老師佈置的作業,每週我將在LeetCode上抽取幸運題進行解答,並編寫題解在博客上。打開LeetCode,因爲我之前嘗試過一道難度爲中等的題,覺得還行,所以這一次我直接找了一道難度爲hard的題,也就是這次我要講解的題Trapping Rain Water 題目如下
大概意思就是給一個向量,告訴柱形圖中每一個黑色的四邊形的高度,然後向整個圖從上到下注水,看能夠盛多少水,一個正方形代表體積爲1。英語比較好的同學請自行翻譯。這道題我一看就想到了解決方法,那就是一層一層的暴力加。我們依靠上圖的例題來解釋一下。首先我們看第一層,從第一個小黑塊開始,記錄一下橫座標,一直到找到下一個小黑塊,這兩個小黑塊中間的空隙長度水佔的體積了,一直循環到向量的尾部,然後加第二層,第三層。寫完後,加上測試代碼,輸入題目中的樣例,輸出正確。心想這種題的難道也能是hard,LeetCode不行啊。興高采烈的提交後發現竟然超時了,虧我還故意優化了一下時間花費。當場懵逼。附上代碼。
class Solution {
public:
int trap(vector<int>& height) {
int ans=0;
int p=-1,q=-1;//記錄橫座標
for(int i=1;i<50000;i++)
{
p=-1,q=-1;
for(int j=0;j<height.size() ;j++)
{
if(height[j]>=i)
{
p=j;//將出現的第一個小黑塊記錄下來
if(q>=0)//如果這不是此行出現的第一個小黑塊
{
ans+=p-q-1;//統計
q=p;//更新起點
}
else
q=p;
}
}
if(q<0)//如果一層只有一個或沒有小黑塊則結束
break;
}
return ans;
}
};
大概想了想時間複雜度,發現我的時間複雜度爲O(nm)(m爲向量中倒數第二大的數)。好吧,我的思路的確是有問題,如果說向量長度只有三,但是數都是十萬多的話問題就很嚴重了,是十分不划算的。說實話這個時候我也懵了,不知所措。然後就只好轉變方向,之前的方法是橫向切割,一層一層計算,那麼縱向切割呢,這樣的話複雜度就是O(n)了,應該可以達到要求,那麼問題來了,怎麼統計水的體積。
一個很簡單的模型,如果兩端都有高度爲a的黑方條,那麼這兩個黑方條之間的空間是可以注滿水的,現在我們向裏面的這個空間填一些小方塊,填一個小方塊的話在其豎直方向的水就會少一體積,如果說我放的小方塊過多而超出了水面呢?設這一很長的條橫座標爲t,高度爲b,考慮t的右邊部分,這一部分的右端高度爲a,左端高度爲b,根據木桶原理在這兩端之間的部分高度爲a是可以全部裝水的。那麼在此區間內再次出現一個高度爲c的長條呢?b>c>a那麼b和c這一段又是剛纔的模型了,利用計算機最擅長的迭代就可以解決啦。
int trap(vector<int>& height) {
int ans=0;
int l=0;//左端點
int r=height.size()-1;//右端點
int m=0;//記錄當前最矮的小黑條
while(r>l)
{
if(height[l]<=m)
{
ans+=m-height[l];
l++;
}
else if(height[r]<=m)
{
ans+=m-height[r];
r--;
}
else
m=min(height[l],height[r]);
}
return ans;
}
從兩端開始,記錄一個當前最矮的,也就是木桶原理中的短板,如果說靠近端點的小黑條的高度低於或齊平,就可以進行一次統計並更新端點,所以判斷條件中的等於很重要,要不然是不會更新端點的。循環就有可能陷入死循環。如果在內部出現了一個更高的小黑條區間的話我們就重新記錄短板,並在此區間繼續上述操作。直到區間長度爲0結束,這時我們的統計也完成了。---------------------------------------------手動分割線-----------------------------------------
see you next illusion