单调栈day47|单调栈理论基础、739. 每日温度、496. 下一个更大元素 I(哈希优化)、503. 下一个更大元素 II(处理循环数组的两种方法)

单调栈day47|单调栈理论基础、739. 每日温度、496. 下一个更大元素 I(哈希优化)、503. 下一个更大元素 II(处理循环数组的两种方法)

单调栈理论基础

  • 单调栈就是栈,只不过内部的元素是单调递增或者单调递减的
  • 使用场景:通常是一维数组,要寻找元素的右边第一个比自己大的元素的位置,就可以用单调栈。时间复杂度为O(n)
  • 作用用栈来记录遍历过的元素
  • 栈内存储的元素是数组的下标,而且如果是元素右边第一个更大元素递增;如果是更小元素,单调栈递减

739. 每日温度

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

示例 1:

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

示例 2:

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

示例 3:

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

提示:

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

1.暴力

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        vector<int> answer(temperatures.size(),0);
        for(int i=0;i<temperatures.size();i++)
            for(int j=i+1;j<temperatures.size();j++)
            {
                if(temperatures[j]>temperatures[i])
                {
                    answer[i]=j-i;
                    break;
                }
                else
                continue;
            }
        return answer;
    }
};

2.单调栈

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        stack<int> st;
        vector<int> answer(temperatures.size(),0);
        st.push(0);
        for(int i=1;i<temperatures.size();i++)
        {
            if(temperatures[i]<=temperatures[st.top()])
            st.push(i);
            else
            {
                while(!st.empty()&&temperatures[i]>temperatures[st.top()])
                {
                    answer[st.top()]=i-st.top();
                    st.pop();
                }
                st.push(i);
            }
        }
        return answer;
    }
};

单调栈实现得很巧妙。

要点:

  • 所有的元素都是通过单调栈才能进入answer的,所有单调栈才是核心,而不是for循环

  • 第一个元素的下标要先入栈,因为它是第一个,没人跟它比,使用只能先入栈

  • 当temperatures[i]>temperatures[st.top()]的时候,要注意**此时需要判断栈是否为空!**当栈为空时,没有栈顶元素了,直接入栈即可(冷知识:如果实现不确定循环次数,要用while,不要用for)

  • vector answer(temperatures.size(),0),初始化的时候直接赋为0,这样时为了给栈最后留存的元素赋值的

496. 下一个更大元素 I

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

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

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

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

示例 1:

输入:nums1 = [4,1,2], nums2 = [1,3,4,2].
输出:[-1,3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:

  • 4 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
  • 1 ,用加粗斜体标识,nums2 = [1,3,4,2]。下一个更大元素是 3 。
  • 2 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。

示例 2:

输入:nums1 = [2,4], nums2 = [1,2,3,4].
输出:[3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:

  • 2 ,用加粗斜体标识,nums2 = [1,2,3,4]。下一个更大元素是 3 。
  • 4 ,用加粗斜体标识,nums2 = [1,2,3,4]。不存在下一个更大元素,所以答案是 -1 。

提示:

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

1.暴力

class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        vector<int> ans(nums2.size(),-1);
        vector<int> result(nums1.size(),-1);
        stack<int> st;
        st.push(0);
        for(int i=0;i<nums2.size();i++)
        {
            if(nums2[i]<=nums2[st.top()])
            st.push(i);
            else 
            {
                while(!st.empty()&&nums2[i]>nums2[st.top()])
                {
                    ans[st.top()]=nums2[i];
                    st.pop();
                }
                st.push(i);
            }
        }
        for(int i=0;i<nums1.size();i++)
            for(int j=0;j<nums2.size();j++)
            {
                if(nums1[i]==nums2[j])
                {
                    result[i]=ans[j];
                    break;
                }
            }
        return result;
    }
};

暴力主要体现在由nums2向nums1转换的过程中,这种转换其实用哈希表来优化:

2.哈希优化

class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        vector<int> result(nums1.size(),-1);
        stack<int> st;
        unordered_map<int,int> map;
        for(int i=0;i<nums1.size();i++)
        map[nums1[i]]=i;
        st.push(0);
        for(int i=1;i<nums2.size();i++)
        {
            if(nums2[i]<=nums2[st.top()])
            st.push(i);
            else
            {
                while(!st.empty()&&nums2[i]>nums2[st.top()])
                {
                    if(map.find(nums2[st.top()])!=map.end())
                    {
                        int index=map[nums2[st.top()]];
                        result[index]=nums2[i];
                    }
                    st.pop();
                }
                st.push(i);
            }
        }
        return result;
    }
};
  • 要熟练掌握哈希表的录入:
unordered_map<int,int> map;
for(int i=0;i<nums1.size();i++)
   map[nums1[i]]=i;

其中,key 为 nums1[i],value 为 i,格式为map[key]=value即可实现哈希存储

  • 也要熟练掌握哈希表的查找(由key查找value):
if(map.find(nums2[st.top()])!=map.end())
{
    int index=map[nums2[st.top()]];
    result[index]=nums2[i];
}

此时,只有当哈希表内部已经存储了,才可以进行查找。所以查找前需要用map.find()来判断是否存在。记录的方式也比较简单:int index=map[nums2[st.top()]],注意是中括号

503. 下一个更大元素 II(处理循环数组的两种方法)

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

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

示例 1:

输入: nums = [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数;
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。

示例 2:

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

提示:

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

1.拼接法

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        vector<int> nums1(nums.begin(),nums.end());
        nums.insert(nums.end(),nums1.begin(),nums1.end());
        vector<int> result(nums.size(),-1);
        stack<int> st;     
        st.push(0);
        for(int i=1;i<nums.size();i++)
        {
            if(nums[i]<=nums[st.top()])
            st.push(i);
            else
            {
                while(!st.empty()&&nums[i]>nums[st.top()])
                {
                    result[st.top()]=nums[i];
                    st.pop();
                }
                st.push(i);
            }
        }
        result.resize(nums.size()/2);
        return result;
    }
};
  • 将nums拼成原来的两倍长度:
vector<int> nums1(nums.begin(),nums.end());
nums.insert(nums.end(),nums1.begin(),nums1.end());

insert可以将范围中的元素插入到指定位置之前,而且这个范围是左闭右闭的区间所以将nums1.end()换成nums1.end()-1也可以。

  • 将nums缩回原来的一半:
result.resize(nums.size()/2);

2.取余法

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        int len=nums.size();
        vector<int> result(len,-1);
        stack<int> st;
        st.push(0);
        for(int i=0;i<len*2;i++)
        {
            if(nums[i%len]<=nums[st.top()%len])
            st.push(i%len);
            else
            {
                while(!st.empty()&&nums[i%len]>nums[st.top()])
                {
                    result[st.top()]=nums[i%len];
                    st.pop();
                }
                st.push(i%len);
            }
        }
        return result;
    }
};

通过画出推导的流程,发现这种方法也是可行的。虽然在之后的循环中,result先前的值会被重新修改,但是新的值和旧的值其实是一样大的。总之,这种方法需要我们动笔画出来,而不是靠脑洞想

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值