目录
一.盛水最多的容器
1.对应letecode链接:
2.题目描述:
3.解题思路:
暴力解我们需要找到两个点使得这个盛水量最大,这样的话我们可以枚举每一个数组中的任意的两个点求得这个盛水量,依次遍历更新这个最大的盛水量 即可。
对应代码:
class Solution { public: int maxArea(vector<int>& height) { int maxAns=0; int N=height.size(); for(int i=0;i<N;i++) { //两层循环用于枚举任意的两个点 for(int j=i+1;j<N;j++) { int tmp=min(height[i],height[j])*(j-i); //获取水量 maxAns=max(maxAns,tmp); } } return maxAns; } };
方法二:双指针
双指针这种解法比较难想到大致流程是如下:
- 定义两个指针分别指向左边和右边并计算此时获得的水量
- 左指针和右指针谁小,谁就先移动直到重合成一个点
对应图解
依次类推即可:对应代码
int maxArea(vector<int>& height) { // write code here if(height.size()<2){ return 0; } int maxAns=0; int L=0; int R=height.size()-1; while(L<R) { maxAns=max(maxAns,min(height[L],height[R])*(R-L)); if(height[L]<height[R]){ L++; } else{ R--; } } return maxAns; }
二.接雨水问题
1.对应letecode链接
2.题目描述:
3.解题思路:
方法一:如果我们能够求得每个位置左边的最大值和右边的最大值那么这个位置的能够接到的水量我们就得到了,每个位置我们都这么干那么我们最后肯定能够得到答案。我们如何快速获得每个位置左边的最大值和右边位置的最大值这里我们可以使用辅助数组提前生成左边的最大值和右边位置的最大值。对应代码:
class Solution { public: int trap(vector<int>& height) { if(height.empty()) { return 0; } int N=height.size(); vector<int>leftMax(N);//生成每个位置左边位置的最大值 leftMax[0]=height[0]; for(int i=1;i<N;i++) { leftMax[i]=max(height[i],leftMax[i-1]); } vector<int>rightMax(N);//生成每个位置右边的最大值 rightMax[N-1]=height[N-1]; for(int i=N-2;i>=0;i--) { rightMax[i]=max(rightMax[i+1],height[i]); } int water=0; for(int i=0;i<N;i++) { int tmp=min(leftMax[i],rightMax[i]); water+=max(0,tmp-height[i]);//能够得到的水量 } return water; } };
方法二:单调栈
看gif图我们可以发现,遍历到某些柱子的时候,会由于和之前的某个柱子形成凹形的坑,接住雨水。这道题目可以用单调栈来做。单调栈就是比普通的栈多一个性质,即维护一个栈内元素单调。
比如当前某个单调递减的栈的元素从栈底到栈顶分别是:[10, 9, 8, 3, 2],如果要入栈元素5,需要把栈顶元素pop出去,直到满足单调递减为止,即先变成[10, 9, 8],再入栈5,就是[10, 9, 8, 5]。对应代码:
class Solution { public: int trap(vector<int>& height) { int water=0; int N=height.size(); stack<int>stk; for(int i=0;i<N;i++) { while(!stk.empty()&&height[i]>height[stk.top()]) { int curIndex=stk.top(); stk.pop(); if(stk.empty())//左边没有比他小的水肯定漏出去了 { break; } int leftIndex=stk.top(); water+=(i-leftIndex-1)*(min(height[leftIndex],height[i])-height[curIndex]); } //相等直接入栈 stk.push(i); } //栈中剩余的元素右边没有比他大的雨也漏出去了 return water; } };
方法三:双指针
- 定义一个左边的最大值和右边的最大值分别指向最左边的位置和右边的位置
- 从1位置开始遍历一直遍历到N-1位置
- 左边和右边的最大值谁小先结算谁,谁先移动这是为什么了。这里显眼没有正确的求得左边和右边的最大值但是左边和右边的最大值不会我们当前的这个最大值还小,这也是就是我们为什么能够结算的原因
对应代码:
class Solution { public: int trap(vector<int>& height) { if(height.size()<2){ return 0; } int n=height.size(); int leftMax=height[0]; int rightMax=height[n-1]; int L=1; int R=n-2; long long water=0; while(L<=R){ if(leftMax<=rightMax){ water+=max(0,leftMax-height[L]); leftMax=max(leftMax,height[L++]); } else{ water+=max(0,rightMax-height[R]); rightMax=max(rightMax,height[R--]); } } return water; } };