接雨水
42. 接雨水 - 力扣(LeetCode)
暴力解法
对计算接到的雨水,有两种方式,一是按照行来计算。
另一种是按列计算
按列计算容易不乱。基本思路如下:
对每列i进行循环,在循环中,找到该列左侧的最大高度leftHeight和该列右侧的最大高度rightHeight,该列能接到的雨水为两者较小值减去该列的高度。
h = min(leftHeight,rightHeight) - height[i]
当h大于0时,将结果累加进最后的返回结果result中.
具体思路可以看代码随想录 (programmercarl.com)
代码如下
class Solution {
public://计算给定高度数组中能接的雨水量int trap(vector<int>& height) {int sum = 0; // sum 用于累计接到的雨水量for(int i = 0; i < height.size(); i++){ // 遍历数组中的每个元素if(i == 0 || i == height.size() - 1) // 数组的第一个和最后一个元素不能接雨水continue; // 跳过这两个元素int rightHeight = height[i]; // 右边最高的柱子高度,初始设为当前柱子高度int leftHeight = height[i]; // 左边最高的柱子高度,初始设为当前柱子高度for(int r = i + 1; r < height.size(); r++){ // 向右扫描,找到右边最高的柱子if(height[r] > rightHeight)rightHeight = height[r];}for(int l = i - 1; l >= 0; l--){ // 向左扫描,找到左边最高的柱子if(height[l] > leftHeight)leftHeight = height[l];}int h = min(leftHeight, rightHeight) - height[i]; // 计算当前位置可以接的雨水量if(h > 0)sum += h; // 如果可以接雨水,则加到总雨水量上}return sum; // 返回总的接雨水量}
};
算法的时间复杂度为O(n^2),对于每个元素都要向左右遍历一次获取最大的leftheight和rightheight,最后2个案例会超时,ac不了,空间复杂度为O(1)。
双指针法
在上述的暴力解法中,如果我们进行调试的话,会发现每次的运行都会左右遍历一次以获取最大的leftheight和rightheight,而这有很多的计算浪费,所以我们可以在开始就从左到右,从右到左遍历一次数组,得到两个数组,分别表示每列i的leftheight和rightheight,然后用上述计算h的方法再计算一次,将算法的时间复杂度降为O(n)。
class Solution {
public:int trap(vector<int>& height) {int sum = 0;vector<int> leftHeight(height.size(), 0); // 创建一个数组来存储每个位置的左边最高柱子高度vector<int> rightHeight(height.size(), 0); // 创建一个数组来存储每个位置的右边最高柱子高度int maxHeight_left_temp = height[0]; // 初始化左边最高柱子高度为第一个元素的高度int maxHeight_right_temp = height[height.size() - 1]; // 初始化右边最高柱子高度为最后一个元素的高度for (int i = 1; i < height.size(); i++) {maxHeight_left_temp = max(maxHeight_left_temp, height[i]); // 更新左边最高柱子高度leftHeight[i] = maxHeight_left_temp; // 存储左边最高柱子高度}for (int i = height.size() - 2; i >= 0; i--) {maxHeight_right_temp = max(maxHeight_right_temp, height[i]); // 更新右边最高柱子高度rightHeight[i] = maxHeight_right_temp; // 存储右边最高柱子高度}// 再次遍历数组,计算每个位置可以接的雨水量for (int i = 0; i < height.size(); i++) {int h = min(leftHeight[i], rightHeight[i]) - height[i]; // 计算当前位置可以接的雨水量if (h > 0)sum += h; // 如果可以接雨水,则加到总雨水量上}return sum;}
};
算法的时间复杂度为O(n),空间复杂度为O(n)(2个长度为n的数组),以空间换时间。
单调栈法
这里想像这样一个问题,接雨水即需要找到当前柱子右侧的大于等于该柱子高度的柱子,然后计算接到的雨水,因此,可以使用单调栈法。
从左到右遍历时,需要找到比当前元素大的元素,考虑使用单调递增栈(什么问题使用什么栈参考之前day52的文章)
class Solution {
public:int trap(vector<int>& height) {stack<int> st; // 创建一个栈来存储柱子的索引int sum = 0; // sum 用于累计接到的雨水量st.push(0); // 先将第一个柱子的索引入栈for (int i = 1; i < height.size(); i++) { // 从第二个柱子开始遍历数组if (height[i] < height[st.top()]) { // 如果当前柱子高度小于栈顶柱子高度st.push(i); // 将当前柱子的索引入栈} else if (height[i] == height[st.top()]) { // 如果当前柱子高度等于栈顶柱子高度st.pop(); // 弹出栈顶元素st.push(i); // 将当前柱子的索引入栈} else { // 如果当前柱子高度大于栈顶柱子高度while (!st.empty() && height[i] > height[st.top()]) { // 当栈不为空且当前柱子高度大于栈顶柱子高度时int mid = st.top(); // 获取栈顶柱子的索引st.pop(); // 弹出栈顶元素if (!st.empty()) { // 如果栈不为空int h = min(height[st.top()], height[i]) - height[mid]; // 计算凹槽的高度int w = i - st.top() - 1; // 计算凹槽的宽度sum += h * w; // 计算凹槽可以接的雨水量,并加到总雨水量上}}}st.push(i); // 将当前柱子的索引入栈}return sum; // 返回总的接雨水量}
};
算法的时间复杂度为O(n),空间复杂度为O(n)。
柱状图中最大的矩形
84. 柱状图中最大的矩形 - 力扣(LeetCode)
暴力解法
类似上题的想法,对每个列i找到左方第一个高度小于它的列left和右方第一个高度小于它的列right,面积为(right - left - 1) * heights[i]。同样,算法的时间复杂度为O(n^2),空间复杂度为O(1)。
class Solution {
public:int largestRectangleArea(vector<int>& heights) {int sum = 0; // sum 用于存储最大矩形面积for (int i = 0; i < heights.size(); i++) { // 遍历数组中的每个元素int left = i; // 初始化左边边界为当前柱子int right = i; // 初始化右边边界为当前柱子// 向左扫描,找到左边第一个高度小于当前柱子的位置for (left = i; left >= 0; left--) {if (heights[left] < heights[i]) break;}// 向右扫描,找到右边第一个高度小于当前柱子的位置for (right = i; right < heights.size(); right++) {if (heights[right] < heights[i]) break;}// 计算以当前柱子为高的矩形面积int w = right - left - 1; // 矩形的宽度int h = heights[i]; // 矩形的高,即当前柱子的高度sum = max(sum, w * h); // 更新最大矩形面积}return sum; // 返回最大矩形面积}
};
超时。
双指针法
有些难度,参考代码随想录代码随想录 (programmercarl.com)
class Solution {
public:int largestRectangleArea(vector<int>& heights) {vector<int> minLeftIndex(heights.size());vector<int> minRightIndex(heights.size());int size = heights.size();// 记录每个柱子 左边第一个小于该柱子的下标minLeftIndex[0] = -1; // 注意这里初始化,防止下面while死循环for (int i = 1; i < size; i++) {int t = i - 1;// 这里不是用if,而是不断向左寻找的过程while (t >= 0 && heights[t] >= heights[i]) t = minLeftIndex[t];minLeftIndex[i] = t;}// 记录每个柱子 右边第一个小于该柱子的下标minRightIndex[size - 1] = size; // 注意这里初始化,防止下面while死循环for (int i = size - 2; i >= 0; i--) {int t = i + 1;// 这里不是用if,而是不断向右寻找的过程while (t < size && heights[t] >= heights[i]) t = minRightIndex[t];minRightIndex[i] = t;}// 求和int result = 0;for (int i = 0; i < size; i++) {int sum = heights[i] * (minRightIndex[i] - minLeftIndex[i] - 1);result = max(sum, result);}return result;}
};
算法的时间复杂度为O(n),空间复杂度也为O(n)。
单调栈法
代码随想录 (programmercarl.com)
明天补