Container With Most Water
Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.
Note: You may not slant the container.
算法1:枚舉容器的兩個邊界,時間復雜度O(n^2)。大數據超時
class Solution { public: int maxArea(vector<int> &height) { int res = 0, n = height.size(); for(int i = 0; i < n; i++)//左邊界 for(int j = i+1; j < n; j++)//右邊界 { int tmp = (j-i)*min(height[i],height[j]); if(res < tmp)res = tmp; } return res; } };
對上面的稍加改進,根據當前的已經計算出來的結果以及左邊界的值,可以算出容器右邊界的下界。不過還是超時
class Solution { public: int maxArea(vector<int> &height) { int res = 0, n = height.size(); for(int i = 0; i < n; i++)//左邊界 { if(height[i] == 0)continue; for(int j = max(i+1, res/height[i]+i); j < n; j++)//右邊界 { int tmp = (j-i)*min(height[i],height[j]); if(res < tmp)res = tmp; } } return res; } };
算法2:時間復雜度O(nlogn)。
構建結構體包含height和height在原數組中的位置
struct Node
{
int height;
int index;
};
對該結構體數組按照height的值遞增排序,假設排序后的數組為vec.
假設f[i] 表示數組vec[i,i+1,…]內所有height按照原來的位置順序排列好以后的最大水量
那么f[i-1]可以在O(1)時間內計算出來:因為vec[i-1].height 小於vec[i,i+1,…]內的所有height,所以以vec[i-1].index為邊界的容器高度為vec[i-1].height,最大水量只需要分別計算vec[i,i+1,…]內按原始位置排列最前面和最后面的height,取兩者的較大值。即下圖中,黑色是最低的,要計算以黑色為邊界的容器的最大水量,只需要分別和第一個和最后一個計算,去兩者較大值
class Solution { struct Node { int height; int index; Node(int h, int i):height(h),index(i){} Node(){} bool operator < (const Node &a)const { return height < a.height; } }; public: int maxArea(vector<int> &height) { int res = 0, n = height.size(); if(n <= 1)return 0; vector<Node>vec(n); for(int i = 0; i < n; i++) { vec[i].index = i; vec[i].height = height[i]; } sort(vec.begin(), vec.end()); int start = vec[n-1].index, end = start;//記錄已經處理完的height的原始位置的左右端點 for(int i = n-2; i >= 0 ; i--) { start = min(start, vec[i].index); end = max(end, vec[i].index); res = max(res, max(vec[i].height*(vec[i].index - start), vec[i].height*(end - vec[i].index))); } return res; } };
算法3:時間復雜度O(n),兩個指針i, j分別從前后向中間移動,兩個指針分別表示容器的左右邊界。每次迭代用當前的容量更新最大容量,然后把高度小的邊界對應的指針往中間移動一位。 本文地址
正確性證明:由於水的容量是有較小的那個邊界決定的,因此某次迭代中,假設height[i] < height[j],那么j 減小肯定不會使水的容量增大,只有i 增加才有可能使水的容量增大。但是會不會有這種可能:當前的i 和 某個k (k > j)是最大容量, 這也是不可能的,因為按照我們的移動規則,既然右指針從k 移動到了j,說明i 的左邊一定存在一個邊界 m,使m > k,那么[m, k]的容量肯定大於[i, k],所以[i,k]不可能是最大容量。可以參考here
class Solution { public: int maxArea(vector<int> &height) { int res = 0, n = height.size(); int left = 0, right = n-1; while(left < right) { res = max(res, (right-left)*min(height[left], height[right])); if(height[left] < height[right]) left++; else right--; } return res; } };
Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.
For example,
Given [0,1,0,2,1,0,1,3,2,1,2,1]
, return 6
.
The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped.
注意和上一題的區別,上一題相當於在每個位置放一個擋板,擋板之間可以蓄水;這一題是放置寬度為1的柱子,柱子上面可以蓄水。
算法4:分析某個柱子,發現該柱子上水的高度和其左右兩邊的最高柱子有關,設該柱子左邊所有柱子中最高的為leftmax,右邊所有柱子中最高的為rightmax,如果min(leftmax, rightmax) 大於該柱子的高度,那么該柱子上可以蓄水為min(leftmax, rightmax) - 該柱子高度,如果min(leftmax, rightmax) <= 該柱子高度,則該柱子上沒有蓄水。
可以從后往前掃描一遍數組求得某個柱子右邊的最高柱子,從前往后掃描一遍數組求得某個柱子左邊的最高柱子, 然后按照上面的分析可以求得所有的蓄水量。
class Solution { public: int trap(int A[], int n) { int res = 0; vector<int>rightMax(n);//柱子右邊最大的柱子高度 int maxv = 0; for(int i = n-1; i >= 0; i--) { rightMax[i] = maxv; maxv < A[i] ? maxv = A[i] : maxv; } maxv = 0; int conHeight; for(int i = 0; i < n; i++) {//此時maxv為柱子i左邊最大的柱子高度 conHeight = min(maxv, rightMax[i]); if(conHeight > A[i]) res += conHeight - A[i]; maxv < A[i] ? maxv = A[i] : maxv; } return res; } };
上面的代碼時間空間復雜度都是O(n),掃描了兩次數組
算法5:可以換一種思路,如下圖,如果我們求出了兩個紅色框的面積,然后再減去框內黑色柱子的面積,就是水的面積了,時間復雜度O(N),空間O(1), 數組掃描2次
如何求紅色框內的面積呢,我們先求出最大的柱子高度,然后左右分開求。求面積是是一層一層的累加
class Solution { public: int trap(int A[], int n) { int maxHeight = 0, maxIdx = 0; for(int i = 0; i < n; i++)//求最大高度 if(A[i] > maxHeight) { maxHeight = A[i]; maxIdx = i; } int height = 0; int pillarArea = 0;//柱子面積 int totalArea = 0;//總面積 for(int i = 0; i < maxIdx; i++) { if(A[i] > height) { totalArea += (A[i] - height)*(maxIdx - i); height = A[i]; } pillarArea += A[i]; } height = 0; for(int i = n-1; i > maxIdx; i--) { if(A[i] > height) { totalArea += (A[i] - height)*(i - maxIdx); height = A[i]; } pillarArea += A[i]; } return totalArea - pillarArea; } };
算法6:參考here,也是和算法5一樣求面積,但是這里利用上一題的左右指針思想,只需要掃描一遍數組
class Solution { public: int trap(int A[], int n) { int left = 0, right = n-1; int totalArea = 0, pillarArea = 0, height = 0; while(left <= right) { if(A[left] < A[right]) { if(A[left] > height) { totalArea += (A[left]-height)*(right - left + 1); height = A[left]; } pillarArea += A[left]; left++; } else { if(A[right] > height) { totalArea += (A[right]-height)*(right - left + 1); height = A[right]; } pillarArea += A[right]; right--; } } return totalArea - pillarArea; } };
【版權聲明】轉載請注明出處:http://www.cnblogs.com/TenosDoIt/p/3812880.html