首页 > 编程语言 >代码随想录算法训练营第42天 | 第九章动态规划 part2

代码随想录算法训练营第42天 | 第九章动态规划 part2

时间:2024-10-15 22:22:47浏览次数:3  
标签:int 随想录 top 42 st part2 ------ height 单调

文章目录

第十章 单调栈 part02

42. 接雨水

接雨水这道题目是面试中特别高频的一道题,也是单调栈应用的题目,大家好好做做。

建议是掌握双指针和单调栈,因为在面试中写出单调栈可能有点难度,但双指针思路更直接一些。

在时间紧张的情况下,能写出双指针法也是不错的,然后可以和面试官慢慢讨论如何优化。

传送门:0042.接雨水

示例数组:

在这里插入图片描述
height = [ 0 , 1 , 0 , 2 , 1 , 0 , 1 , 3 , 2 , 1 , 2 , 1 ] \text{height} = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1] height=[0,1,0,2,1,0,1,3,2,1,2,1]

过程解释表格:

iheight[i]栈内容 (st)栈中高度 (对应 height)弹出 mid 下标左边界 (st.top())右边界 (i)宽度 (w)高度 (h)当前累计水量总水量 (sum)
00[0][0]------0
11[0, 1][0, 1]------0
20[0, 1, 2][0, 1, 0]------0
32[0, 1][0, 1]2131111
32[0][0]1032001
32[0, 3][0, 2]------1
41[0, 3, 4][0, 2, 1]------1
50[0, 3, 4, 5][0, 2, 1, 0]------1
61[0, 3, 4][0, 2, 1]5461112
61[0, 3, 4, 6][0, 2, 1, 1]------2
73[0, 3, 4][0, 2, 1]6472124
73[0, 3][0, 2]4373137
73[0][0]3076007
73[0, 7][0, 3]------7
82[0, 7, 8][0, 3, 2]------7
91[0, 7, 8, 9][0, 3, 2, 1]------7
102[0, 7, 8][0, 3, 2]98101118
102[0, 7][0, 3]87102008
102[0, 7, 10][0, 3, 2]------8
111[0, 7, 10, 11][0, 3, 2, 1]------8

过程解析:

  1. 栈中高度的追踪:每当一个新柱子入栈时,我们不仅记录其索引,还可以追踪其对应的高度。这样可以很直观地看出栈中保存的每个柱子的高度是如何变化的。

  2. 形成凹槽时弹出的逻辑:每次当 height[i] > height[st.top()] 时,我们弹出栈顶元素(即 mid),并用栈中新的顶元素(st.top())作为左边界,当前元素 i 作为右边界,计算出宽度和高度差,然后累加水量。

  3. 水量计算示例

    • i = 3 时,弹出 2,左右边界分别是 13,宽度为 1,高度差为 1,水量为 1
    • i = 7 时,弹出多个柱子形成多个凹槽,水量分别累加。

这题确实挺难的,看了一遍视频后,再看文字,依然搞不懂。琢磨了半天。还是又看了一遍,才又懂了一点。确实对于不懂的东西,不要硬干,要老老实实的再看一遍。

 stack<int> st;
        int sum=0;
        for(int i=0;i<height.size();i++)
        {
            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;
    }
            }//注意while还在循环,因为右边多了一组墙,左边多了几组雨水        
            st.push(i);//把当前这个最大值扔进去,当作左边的墙
        }
        return sum;
    }

部分内容看代码注释。

双指针法

class Solution {
public:
    int trap(vector<int>& height) {
    int sum=0;
    vector<int> maxLeft(height.size(), 0);
    vector<int> maxRight(height.size(), 0);

    for(int i=1;i<height.size()-1;i++)
    {
          maxLeft[i]=max(maxLeft[i-1],height[i-1]);     
    }
    for(int i=height.size()-2;i>0;i--)
    {
          maxRight[i]=max(maxRight[i+1],height[i+1]);     
    }
    for(int i=1;i<height.size()-1;i++)
    {
          sum+=max(min(maxLeft[i],maxRight[i])-height[i],0);     
    }
    return sum;
    }
};

双指针法还是比较好理解的。稍微用点动态规划的简单知识。记录每个柱子左边柱子最大高度,记录每个柱子右边柱子最大高度,求和。三个for循环。简简单单。

84. 柱状图中最大的矩形

有了之前单调栈的铺垫,这道题目就不难了。

双指针法

class Solution {
public:
    int largestRectangleArea(vector<int>& height) {
 int result=0;
 int n=height.size();
    vector<int> minLeft(n, 0);
    vector<int> minRight(n, 0);
    minLeft[0] = -1;
    for(int i=1;i<n;i++)
    {
        int t = i - 1;
        while (t >= 0 && height[t] >= height[i]) t = minLeft[t];
            minLeft[i] = t; 
    }
    minRight[n - 1] = n; 
    for(int i=n-2;i>=0;i--)
    {
         int t = i + 1;
            // 这里不是用if,而是不断向右寻找的过程
            while (t < n && height[t] >= height[i]) t = minRight[t];
            minRight[i] = t;
    }
    for(int i=0;i<n;i++)
    {
         int sum = height[i] * (minRight[i] - minLeft[i] - 1);
            result = max(sum, result);
    }
    return result;
    }
    
};

双指针法还是比较简单的。最重要的是知道循环找右边的思路

单调栈法

传送门:0084.柱状图中最大的矩形
在 height数组上后,都加了一个元素0, 为什么这么做呢?

首先来说末尾为什么要加元素0?

如果数组本身就是升序的,例如[2,4,6,8],那么入栈之后 都是单调递减,一直都没有走 情况三 计算结果的哪一步,所以最后输出的就是0了。
那么结尾加一个0,就会让栈里的所有元素,走到情况三的逻辑。

开头为什么要加元素0?

如果数组本身是降序的,例如 [8,6,4,2],在 8 入栈后,6 开始与8 进行比较,此时我们得到 mid(8),right(6),但是得不到 left。

(mid、left,right 都是对应版本一里的逻辑)

因为 将 8 弹出之后,栈里没有元素了,那么为了避免空栈取值,直接跳过了计算结果的逻辑。

之后又将6 加入栈(此时8已经弹出了),然后 就是 4 与 栈口元素 6 进行比较,周而复始,那么计算的最后结果result就是0。

最后发现,单调栈的思路有点像砍山头。逐渐把前面的山头给砍平。看懂下面的过程,差不多

1352131面积
1352
13525
13比前大26
1比前大比前大23
1比前大比前大212
1比前大比前大212
1比前大比前大比前大13
1比前大比前大比前大1313
1比前大比前大比前大1比前大16
11111117
class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        stack<int> st;
        st.push(0);
        heights.insert(heights.begin(), 0); // 数组头部加入元素0
        heights.push_back(0); // 数组尾部加入元素0
    
        int result = 0;
        int n=heights.size();
        for(int i=1;i<n;i++)
        {
        while (heights[i] < heights[st.top()]) {
                int mid = st.top();//最顶就是st.top()
                st.pop();//用了就扔了,不用再管他了,它已经到极限了
                int w = i - st.top() - 1;//确定宽
                int h = heights[mid];//确定高
                result = max(result, w * h);//比较是否最大
            }//注意一直循环,只有有更大的就削山头。直到单调栈。
            st.push(i);//加入进去,准备下一个
        }
    return result;
    }
    
};

终于至少把《代码随想录》这本书刷完了。自己效率确实挺低的,两个多月才刷到这。图论不打算刷了,一方面卡玛网不太喜欢,一方面前面够我消化的了,还是不太想刷图论了。还是很有收获。确实自己速度很慢。但也已经尽力了。最重要的还是感觉自己没有什么动力,感觉自己刷题更多的是为了逃避现实吧。反而没有经历搞自己的学业。难搞,反正先暂停一段时间,搞搞课题。

标签:int,随想录,top,42,st,part2,------,height,单调
From: https://blog.csdn.net/zxjiaya/article/details/142936795

相关文章