單調棧、動態規劃
【題目】
給定 n 個非負整數表示每個寬度爲 1 的柱子的高度圖,計算按此排列的柱子,下雨之後能接多少雨水。
【輸入】: [0,1,0,2,1,0,1,3,2,1,2,1]
【輸出】: 6
【解法一】:暴力解法
思路:對於數組中的每個元素,我們找出下雨後水能達到的最高位置,等於兩邊最大高度的較小值減去當前高度的值。每次計算的當前元素能存儲的雨水,最後累計即可
- 首尾元素無須計算
- 當左右無大於當前元素的值值,當前位置無法存儲雨水
public int method1(int[] arr) {
if(arr.length==0 || arr==null) return 0;
int i,j,res,left,right,leftMax,rightMax;
res = 0;
for (i=1; i<arr.length-1; i++) {
left = i;
right = i;
leftMax = rightMax = 0;
// 向左尋找大於當前元素的最大值
while (left>=0) {
if (arr[left] > arr[i] && leftMax < arr[left]) leftMax = arr[left];
left--;
}
// 向右尋找大於當前元素的最大值
while (right<arr.length) {
if (arr[right] > arr[i] && rightMax < arr[right]) rightMax = arr[right];
right++;
}
if (leftMax != 0 && rightMax != 0) {
res += Math.min(leftMax,rightMax) - arr[i];
}
}
return res;
}
【解法二】動態規劃
在暴力方法中,我們僅僅爲了找到最大值每次都要向左和向右掃描一次。但是我們可以用動態規劃的方式提前存儲這個值。
/**
* 在暴力方法中,我們僅僅爲了找到最大值每次都要向左和向右掃描一次。
* 但是我們可以提前存儲這個值。因此,可以通過動態編程解決。
*
* dp1[x] 從0達到x的時候,已有的最大值
* dp2[x] 從n-1到達x的時候,已有的最大值
* */
public int method2(int[] a) {
if(a.length==0 || a==null) return 0;
int i,res;
int[] dp1,dp2;
dp1 = new int[a.length];
dp2 = new int[a.length];
dp1[0] = a[0];
dp2[a.length-1] = a[a.length-1];
for (i=1; i<a.length; i++) {
dp1[i] = Math.max(dp1[i-1],a[i]);
}
for (i=a.length-2; i>=0; i--) {
dp2[i] = Math.max(dp2[i+1],a[i]);
}
res = 0;
for (i=1; i<a.length-1; i++) {
if (a[i]<dp1[i] && a[i]<dp2[i]) {
res += Math.min(dp1[i],dp2[i]) - a[i];
}
}
return res;
}
【解法三】單調棧
/**
* 利用單調棧
* 棧頂元素大於當前元素,當前元素入棧
* 等於和小於要進行相應的計算
*
* 單調棧存下標要比存內容的意義更大,因爲可以通過下標定位到元素
* */
public int method3(int[] a) {
if (a==null || a.length==0) return 0;
Stack<Integer> stack = new Stack<>();
int i,res;
res = 0;
for (i=0; i<a.length; i++) {
while (!stack.empty() && a[stack.peek()] < a[i]) {
int curIndex = stack.pop();
// 需要把相同的元素全部彈出棧
while (!stack.empty() && a[curIndex] == a[stack.peek()]) {
curIndex = stack.pop();
}
if (!stack.empty()) {
int leftIndex = stack.peek();
int height = Math.min(a[i],a[leftIndex])-a[curIndex];
int width = i - leftIndex - 1;
res += height * width;
}
}
stack.add(i);
}
return res;
}
注意:單調棧存下標要比存內容的意義更大,因爲可以通過下標定位到元素