代码随想录-单调栈|ACM模式

代码随想录感想

​​​​​​739. 每日温度

题目描述:

输入输出示例:

思路和想法:

496. 下一个更大元素 I

题目描述:

输入输出示例:

思路和想法:

503. 下一个更大元素 II

题目描述:

输入输出示例:

思路和想法:

42.接雨水

题目描述:

输入输出示例:

思路和想法:

84. 柱状图中最大的矩形

题目描述:

输入输出示例:

思路和想法:


​​​​​​739. 每日温度

题目描述:

给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。

输入输出示例:

输入: temperatures = [73,74,75,71,69,72,76,73] 输出: [1,1,4,2,1,1,0,0]

输入: temperatures = [30,40,50,60] 输出: [1,1,1,0]

输入: temperatures = [30,60,90] 输出: [1,1,0]

提示:

  • 1 <= temperatures.length <= 105
  • 30 <= temperatures[i] <= 100

思路和想法:

这一道题目是单调栈的模板题,做这道题目可以很好的理解单调栈。

何时想起用单调栈:通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置。

在使用单调栈的时候首先要明确几点:

  1. 单调栈里存放的元素是什么?这道题目放入数组的索引。
  2. 单调栈里元素是递增,还是递减?不确定可以模拟数据入栈和出栈的过程,就可以明晰。
#include

using namespace std;

/*
* 作者:希希雾里
* 739. 每日温度 I
* 讲述自己的一些理解和认知
* */

/*单调栈模板题目
* 时间复杂度:O(n)   空间复杂度:O(n)
* */
vector dailyTemperatures(vector& temperatures) {
    vector result(temperatures.size(),0);
    stack st;

    for (int i = 0; i < temperatures.size(); ++i) {
        while (!st.empty() && temperatures[i] > temperatures[st.top()]){
            int mid = st.top();
            st.pop();
            result[mid] = i - mid;
        }
        st.push(i);
    }
    return result;
}

int main() {
    vector number;
    int num;
    while (cin >> num){
        number.push_back(num);
        if(getchar() == '\n'){
            break;
        }
    }

    //这里输出注意,最后一位要注意单独处理。(经常会出现的问题)
    vector result;
    result = dailyTemperatures(number);
    for (int i = 0; i < result.size() - 1; ++i) {
        cout << result[i] << " ";
    }
    cout << result[result.size() - 1];
    return 0;
}

/* 测试样例,列清楚情况
73 74 75 71 69 72 76 73
1 1 4 2 1 1 0 0

30 40 50 60
1 1 1 0

30 60 90
1 1 0
* */

496. 下一个更大元素 I

题目描述:

nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧第一个 比 x 大的元素。

给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。

对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。

返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素

输入输出示例:

输入:nums1 = [4,1,2], nums2 = [1,3,4,2]. 输出:[-1,3,-1]

输入:nums1 = [2,4], nums2 = [1,2,3,4]. 输出:[3,-1]

提示:

  • 1 <= nums1.length <= nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 104
  • nums1和nums2中所有整数 互不相同
  • nums1 中的所有整数同样出现在 nums2 中

思路和想法:

这道题目相较于上一道模板题的升级的地方在于,需要查找元素。无重复元素,这里可以使用map来实现映射,根据数值快速找到下标,并判断nums2[i]是否在nums1中出现过

#include

using namespace std;

/*
* 作者:希希雾里
* 496. 下一个更大元素 I
* 讲述自己的一些理解和认知
* 查找(map) + 单调栈
* 上述思路,跟单调栈的内核很好的契合
* */

/*查找 + 单调栈*/
vector nextGreaterElement(vector& nums1, vector& nums2) {
    vector result(nums1.size(), -1);

    stack st;
    //构建映射,数值-下标
    unordered_map mp;
    for (int i = 0; i < nums1.size(); ++i) {
        mp[nums1[i]] = i;
    }
    
	//单调栈记录
    for (int i = 0; i < nums2.size(); ++i) {
        while(!st.empty() && nums2[i] > nums2[st.top()]){
            if (mp.count(nums2[st.top()]) > 0){
                int index = mp[nums2[st.top()]];
                result[index] = nums2[i];
            }
            st.pop();
        }
        st.push(i);
    }
    return result;
}

int main() {
    vector number1;
    vector number2;
    int num;
    while (cin >> num){
        number1.push_back(num);
        if(getchar() == '\n'){
            break;
        }
    }
    while (cin >> num){
        number2.push_back(num);
        if(getchar() == '\n'){
            break;
        }
    }

    //这里输出注意,最后一位要注意单独处理。(经常会出现的问题)
    vector result;
    result = nextGreaterElement(number1, number2);
    for (int i = 0; i < result.size() - 1; ++i) {
        cout << result[i] << " ";
    }
    cout << result[result.size() - 1];
    return 0;
}

/* 测试样例,列清楚情况
4 1 2
1 3 4 2
-1 3 -1

2 4
1 2 3 4
3 -1
* */

503. 下一个更大元素 II

题目描述:

给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素

数字 x 的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1 。

输入输出示例:

输入: nums = [1,2,1] 输出: [2,-1,2]

输入: nums = [1,2,3,4,3] 输出: [2,3,4,-1,4]

提示:

  • 1 <= nums.length <= 104
  • -109 <= nums[i] <= 109

思路和想法:

这道题目在每日温度的基础上,进阶要循环数组。

初步想法:直接将两个数组拼接在一起就可以了。这个方法比较直观,但做了一些无用的操作,第一步修改nums数组,而且最后还要把数组resize回去。

其实可以应用取余操作,就可以很好的处理解决。

#include

using namespace std;

/*
* 作者:希希雾里
* 503. 下一个更大元素 II
* 讲述自己的一些理解和认知
* 核心:循环数组遍历,每一个元素的下一个更大的元素。
* */

/*单调栈*/
vector nextGreaterElements(vector& nums) {
    stack st;
    vector result(nums.size(), -1);

    for (int i = 0; i < 2 * nums.size(); ++i) {
        while(!st.empty() && nums[i % nums.size()] > nums[st.top()]) {
            result[st.top()] = nums[i % nums.size()];
            st.pop();
        }
        st.push(i % nums.size());
    }
    return result;
}

int main() {
    vector number;
    int num;
    while (cin >> num){
        number.push_back(num);
        if(getchar() == '\n'){
            break;
        }
    }

    //这里输出注意,最后一位要注意单独处理。(经常会出现的问题)
    vector result;
    result = nextGreaterElements(number);
    for (int i = 0; i < result.size() - 1; ++i) {
        cout << result[i] << " ";
    }
    cout << result[result.size() - 1];
    return 0;
}

/* 测试样例,列清楚情况
1 2 1
2 -1 2

1 2 3 4 3
2 3 4 -1 4
* */

42.接雨水

题目描述:

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

输入输出示例:

代码随想录-单调栈|ACM模式_第1张图片

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1] 输出:6

解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

输入:height = [4,2,0,3,2,5] 输出:9

提示:

  • n == height.length
  • 1 <= n <= 2 * 104
  • 0 <= height[i] <= 105

思路和想法:

这道题目比较好想到的就是暴力法,以列计算遍历,寻找到这一列的左右最高柱即可,只需要计算h = min(leftheight, rightheight) - height[i]。

但是我们能看到就是这个方法的弊病,每计算一个列都得遍历一遍。

这里我们就能够想到是否可以应用到单调栈,是可以的,但还用列的方式,直接获取到左右两侧的最高柱不可行了。

这里需要按照行计算,雨水高度 = min(凹槽左边高度,凹槽右边高度) - height[i]。这里凹槽左边高度是存储在栈内,凹槽右边高度即要入栈的元素。

#include 

using namespace std;
/*
* 作者:希希雾里
* 42.接雨水
* 讲述自己的一些理解和认知
* 单调栈:保持栈内元素有序。从大到小,从小到大
* 接雨水最核心的思路:在于找到左右两边的最高柱子
* */

/*  暴力法
*  使用暴力法可以很好的理解本题的核心思路
*  时间复杂度:O(n^2)         空间复杂度:O(1)
* */
int trap(vector& height) {
    int result = 0;

    for (int i = 1; i < height.size() - 1; ++i) {
        int leftheight = height[i];
        int rightheight = height[i];

        //向左遍历,得到左柱子
        for (int l = i - 1; l >= 0; --l) {
            if(height[l] > leftheight) leftheight = height[l];
        }

        //向右遍历,得到右柱子
        for (int r = i + 1; r <= height.size() - 1; ++r) {
            if(height[r] > rightheight) rightheight = height[r];
        }

        int h = min(leftheight, rightheight) - height[i];
        if(h > 0) result += h;
    }

    return result;
}

/*
*  单调栈
*  可以由暴力法的过程中,感受到寻找左右柱子的过程中
*  我们能发现单调栈就非常的适合。
*  按行计算接水的面积
*  模拟单调栈的流程
* */
int trap2(vector& height) {
    int result = 0;
    stack st;
    if(height.size() <= 2) return result;
    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;
                    result += h * w;
                }
            }
        }
        st.push(i);
    }
    return result;
}

int main() {
    vector num;
    int rain;
    while(cin >> rain){
        num.push_back(rain);
        if(getchar() == '\n'){
            break;
        }
    }

    int result = trap2(num);
    cout << result;

}

/*  测试样例
0 1 0 2 1 0 1 3 2 1 2 1
6

4 2 0 3 2 5
9
*
*
* */

84. 柱状图中最大的矩形

题目描述:

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

输入输出示例:

代码随想录-单调栈|ACM模式_第2张图片

输入:heights = [2,1,5,6,2,3] 输出:10

代码随想录-单调栈|ACM模式_第3张图片

输入: heights = [2,4] 输出: 4

提示:

  • 1 <= heights.length <=105
  • 0 <= heights[i] <= 104

思路和想法:

这道题目和上一道接雨水非常的相近。

#include

using namespace std;

/*
* 作者:希希雾里
* 84.柱状图中最大的矩形
* 讲述自己的一些理解和认知
* 单调栈的核心:寻找左右最近大于或小于的值
* */
/*暴力解法*/
int largestRectangleArea(vector& heights) {
    int sum = 0;
    for (int i = 0; i < heights.size(); i++) {
        int left = i;
        int right = i;
        /*暴力遍历找到左柱和右柱*/
        //循环边界的讨论,left:-1~height.size() - 1   right:0~height.size()
        for (; left >= 0; left--) {
            if (heights[left] < heights[i]) break;
        }
        for (; 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;
}

/*双指针法*/
int largestRectangleArea2(vector& heights) {
    vector minLeftIndex(heights.size());
    vector 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;
}


/*单调栈*/
int largestRectangleArea3(vector& heights) {
    int result = 0;
    stack st;
    //数组头部和尾部加入元素0
    heights.push_back(0);
    heights.insert(heights.begin(),0);
    st.push(heights[0]);


    //着重关注下角标
    for (int i = 1; i < heights.size(); ++i) {
        if(heights[i] > heights[st.top()]){             //第一种情况
            st.push(i);
        } else if(heights[i] == heights[st.top()]){     //第二种情况
            st.pop();
            st.push(i);
        }else{
            while (!st.empty() && heights[i] < heights[st.top()]){  //第三种情况
                int ref = st.top();
                st.pop();
                if(!st.empty()){
                    int left = st.top();
                    int right = i;
                    int w = right - left - 1;
                    int h = heights[ref];
                    result = max(result, h * w);
                }
            }
            st.push(i);
        }
    }
    return result;
}

int main() {
    vector number;
    int num;
    while (cin >> num){
        number.push_back(num);
        if(getchar() == '\n'){
            break;
        }
    }

    int result;
    result = largestRectangleArea3(number);
    cout << result;
    return 0;
}

/* 测试样例
2 1 5 6 2 3

2 4

2 1 1 1 1 1
* */

你可能感兴趣的:(代码随想录感想,算法,数据结构,leetcode)