1. 程式人生 > >演算法題系列之四 - 柱狀圖儲水

演算法題系列之四 - 柱狀圖儲水

題目:

給定一個數組,每個位置的值代表一個高度,那麼整個陣列可以看做是一個直方圖,

如果把這個直方圖當作容器的話,求這個容器能裝多少水

例如:3,1,2,4

代表第一個位置高度為3,第二個位置高度為1,以此類推,這個直方圖能裝3格水。如圖紅色地方:

柱狀圖儲水問題

 

思路:很多人會誤想到正出什麼波峰波谷,這就從開始就錯了,比如兩個相鄰的波峰之外還有更大的波峰,這麼說來你中間這連個波峰波谷算的值多白算了,這個用直接點的想法來做就可以了,就找當前i位置上能裝多少水,就是從i位置向前和後遍歷,找到前後max值較小的減去當前i位置的值就是能裝的水,當然要是前或後沒找到i位置小的,那麼就不能裝水(給你5分鐘畫圖理解下這個思想)。思路有了,考慮解法:

1、暴力解法,每個i位置,都前後遍歷,這個方法的時間複雜度為O(n2),

public static int getWater(int[] arr) {
		if (arr == null || arr.length < 3) {
			return 0;
		}
		int sum = 0;
		for (int i = 1; i < arr.length - 1; i++) {
			int leftMax = arr[0];
			for (int j = 0; j < i; j++) {
				if (arr[j] > leftMax) {
					leftMax = arr[j];
				}
			}
			int rightMax = arr[arr.length - 1];
			for (int k = arr.length - 1; k > i; k--) {
				if (arr[k] > rightMax) {
					rightMax = arr[k];
				}
			}
			sum += Math.max(0, Math.min(leftMax, rightMax) - arr[i]);
		}
		return sum;
	}

2,空間換時間,預處理陣列,在找i之前,定義一個0-i位置最大大值陣列,做法就是右滑陣列,再定義一個i-length-1的最大是陣列,做法就是左滑陣列,然後找i上能裝的水時,不用前後找,只需要查表就可以,這個時間複雜度為O(n),空間複雜度為O(n)。

3、時間複雜度為O(n),空間複雜度為O(1),厲害了這個,想不想聽,想不想學,定義一個左指標,指向第二個元素,一個有指標,指向倒數第二個元素,因為一個和最後一個肯定不能儲水,設定左邊最大值為arr[0],右邊最大值為arr[arr.length-1],只需要判斷左邊最大值與右邊最大值即可,當左邊最大值小於右邊最大值,左指標右滑,左指標位置上能裝的水就是左邊對大值減去左指標指的值,若左指標指向的值大於左邊大值,就不減,說明不能儲水,更新左邊最大值,當右邊最大值小於左邊最大值時,右指標左滑,做法跟前類似,直到左指標小於等於有指標跳出迴圈。反正就一句話,哪邊小那邊指標移動:

public static int getWater(int[] arr) {
		if (arr == null || arr.length < 3) {
			return 0;
		}
		int value = 0;
		int leftMax = arr[0];
		int rightMax = arr[arr.length - 1];
		int l = 1;
		int r = arr.length - 2;
		while (l <= r) {
			if (leftMax <= rightMax) {
				value += Math.max(0, leftMax - arr[l]);
				leftMax = Math.max(leftMax, arr[l++]);
			} else {
				value += Math.max(0, rightMax - arr[r]);
				rightMax = Math.max(rightMax, arr[r--]);
			}
		}
		return value;
	}

轉自:https://yq.aliyun.com/ziliao/300930