抱佛脚一时爽,一直抱佛脚一直爽!这篇文章总结常见的统计图问题,这里的统计图是我捏造的名字...就是指接雨水、买卖股票这种条形图/折线图的题目~
参考链接:leetcode 剑指offer
接雨水
接水最多的容器(lc11)
- 双指针,每次把更矮的那个壁往中间移动一格
int maxArea(vector<int>& height) {
int n = height.size();
if (n <= 1) return 0;
int left = 0, right = n - 1, res = 0;
while (left < right) {
res = max(res, min(height[left], height[right]) * (right - left));
if (height[left] <= height[right]) ++left;
else --right;
}
return res;
}
接雨水(lc42)
- 思路:每个柱子上方能储存的水是由它左边的最大值(含自己)和右边的最大值(含自己)中的较小值决定的
- 法一:动态规划求max_left和max_right数组
int trap(vector<int>& height) {
int n = height.size();
if (!n) return 0;
vector<int> max_right(n);
vector<int> max_left(n);
max_right[n - 1] = height[n - 1];
max_left[0] = height[0];
for (int i = 1; i < n; ++i) {
max_left[i] = max(max_left[i - 1], height[i]);
max_right[n - i - 1] = max(max_right[n - i], height[n - i - 1]);
}
int res = 0;
for (int i = 1; i < n - 1; ++i) {
res += min(max_left[i], max_right[i]) - height[i];
}
return res;
}
- 法二:双指针
int trap(vector<int>& height) { // 每个柱子上方能储存的水是由它左边的最大值和右边的最大值中的较小值决定的
int n = height.size();
if (!n) return 0;
int res = 0;
int left_max = 0, right_max = 0; // left_max:左边的最大值,它是从左往右遍历找到的
int left = 0, right = n - 1; // left:从左往右处理的当前下标
while (left <= right) {
if (left_max <= right_max) {
res += max(0, left_max - height[left]); // 从左往右处理时,left_max是可信的
left_max = max(left_max, height[left]);
++left;
} else {
res += max(0, right_max - height[right]);
right_max = max(right_max, height[right]);
--right;
}
}
return res;
}
买卖股票
只能交易一次(lc121)
- 思路:从右往左扫描,更新res和maxRight;第i天买入股票能得到的最大利润=当前maxRight-prices[i]
int maxProfit(vector<int>& prices) {
int n = prices.size();
if (n < 2) return 0;
int maxRight = prices[n - 1], res = 0;
for (int i = n - 2; i >= 0; --i) {
res = max(res, maxRight - prices[i]);
maxRight = max(maxRight, prices[i]);
}
return res;
}
可以交易无数次(lc122)
- 思路:从左至右扫描,只要prices[i]<prices[i+1]就进行交易
int maxProfit(vector<int>& prices) {
int n = prices.size();
if (n <= 1) return 0;
int res = 0;
for (int i = 0; i < n - 1; ++i) {
if (prices[i] < prices[i + 1]) res += prices[i + 1] - prices[i];
}
return res;
}
其他题目
柱状图中的最大矩形(lc84)
- 思路:遍历数组,每找到一个局部峰值(只要当前的数字大于后面的一个数字,那么当前数字就看作一个局部峰值,跟前面的数字大小无关),然后向前遍历所有的值,算出共同的矩形面积,每次对比保留最大值
int largestRectangleArea(vector<int>& heights) {
int res = 0, n = heights.size();
for (int i = 0; i < n; ++i) {
if (i + 1 < n && heights[i] <= heights[i + 1]) continue;
int minH = heights[i];
for (int j = i; j >= 0; --j) {
minH = min(minH, heights[j]);
res = max(res, minH * (i - j + 1));
}
}
return res;
}