leetcode---栈类问题总结

1.每日温度

请根据每日 气温 列表,重新生成一个列表。对应位置的输出为:要想观测到更高的气温,至少需要等待的天数。如果气温在这之后都不会升高,请在该位置用 0 来代替。

例如,给定一个列表 temperatures = [73, 74, 75, 71, 69, 72, 76, 73],你的输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。

提示:气温 列表长度的范围是 [1, 30000]。每个气温的值的均为华氏度,都是在 [30, 100] 范围内的整数。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/daily-temperatures
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

问题分析:建立一个栈,用来存储temperatures的下标。遍历数组,将数组和栈顶元素比较,如果栈为空则将该元素的下标入栈;如果栈不为空,且以栈顶元素为下标的元素比当前元素的值小,则以栈顶元素为下标的元素的等待天数就为当前元素下标-栈顶元素,同时让栈顶元素出栈,继续用该元素与栈顶元素比较;如果该元素小于栈顶元素则该元素的下标入栈。

代码描述

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& T) {
        //定义一个栈,用于存储T的下标
        stack<int> mystack;
        vector<int> ret(T.size(),0);
        //当将要入栈的元素比栈顶元素大时,用该元素的下标减去栈顶元素的下标就是天数
        for(int i = 0;i < T.size();i++)
        {
            if(mystack.empty())
                mystack.push(i);
            else if(T[mystack.top()] < T[i])
            {
                //找到最近一个比他大的
                ret[mystack.top()] = i - mystack.top();
                mystack.pop();
                i--;
            }
            else if(T[mystack.top()] >= T[i])
                mystack.push(i);
        }
        //栈中剩余元素的的等待天数应该都是0
        while(!mystack.empty())
        {
            ret[mystack.top()] = 0;
            mystack.pop();
        }
        return ret;
    }
};

2.行星碰撞

给定一个整数数组 asteroids,表示在同一行的行星。对于数组中的每一个元素,其绝对值表示行星的大小,正负表示行星的移动方向(正表示向右移动,负表示向左移动)。每一颗行星以相同的速度移动。找出碰撞后剩下的所有行星。碰撞规则:两个行星相互碰撞,较小的行星会爆炸。如果两颗行星大小相同,则两颗行星都会爆炸。两颗移动方向相同的行星,永远不会发生碰撞。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/asteroid-collision
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

问题分析

可能发生碰撞的情况有:前一个行星向右移动,后一个行星向左移动;因此,可以创建一个栈,当栈为空时,让行星直接入栈;栈不为空时,让栈顶元素和计较入栈的元素进行比较,判断是否会发生碰撞,如果发生碰撞判断哪个行星会爆炸即可。

最后,遍历完数组后,栈中剩余的元素就是碰撞完后剩下的行星,将这些行星出栈保存在数组中。出栈顺序为逆序,因此还要将数组逆置得到最终结果。

代码描述

class Solution {
public:
    vector<int> asteroidCollision(vector<int>& asteroids) {
        //遍历数组,让数组元素入栈,入栈时如果栈顶元素与入栈元素值相反,则较小的出栈
        stack<int> mystack;
        for(int i = 0;i < asteroids.size();i++)
        {
            //如果栈中没有元素,就让该元素直接入栈
            if(mystack.empty())
                mystack.push(asteroids[i]);
            //判断栈顶元素和要入栈元素的大小和正负
            else if(asteroids[i] < 0 && mystack.top() > 0)
            {
                //大小相等同时爆炸
                if(-asteroids[i] == mystack.top())
                    mystack.pop();
                else if(asteroids[i] < 0 && -asteroids[i] > mystack.top())
                {
                    //栈顶行星小,爆炸。
                    mystack.pop();
                    i--;
                }
            }
            //如果两个行星运动方向相同,则入栈
            else if((mystack.top() > 0 && asteroids[i] > 0) || (mystack.top() < 0 && asteroids[i] < 0))
                mystack.push(asteroids[i]);
            else if(mystack.top() < 0 && asteroids[i] > 0)
                mystack.push(asteroids[i]);
        }
        vector<int> ret;
        //将栈中的元素存入数组(逆序)
        while(!mystack.empty())
        {
            ret.push_back(mystack.top());
            mystack.pop();
        }
        //将数组逆置,得到最终结果
        vector<int>::iterator begin = ret.begin();
        vector<int>::iterator end = ret.end();
        reverse(begin,end);
        return ret;
    }
};

3.字符串解码

给定一个经过编码的字符串,返回它解码后的字符串。编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/decode-string
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

问题分析

解决这类问题,最重要的就是判断入栈条件,出栈条件及出站后该如何做?

本题中可能出现括号嵌套的情况,比如 2[a2[bc]],这种情况下我们可以先转化成 2[abcbc],在转化成 abcbcabcbc。我们可以把字母、数字和括号看成是独立的 TOKEN,并用栈来维护这些 TOKEN。具体的做法是,遍历这个栈:如果当前的字符为数位,解析出个数字(连续的多个数位)并进栈如果当前的字符为字母或者左括号,直接进栈如果当前的字符为右括号,开始出栈,一直到左括号出栈,出栈序列反转后拼接成一个字符串,此时取出栈顶的数字(此时栈顶一定是数字,想想为什么?),就是这个字符串应该出现的次数,我们根据这个次数和字符串构造出新的字符串并进栈;重复如上操作,最终将栈中的元素按照从栈底到栈顶的顺序拼接起来,就得到了答案。

代码描述

class Solution {
public:
    string decodeString(string s) {
        stack<string> mystack;
        for(auto e:s)
        {
            if(e != ']')
                mystack.push(string() += e);
            //如果是],则开始匹配左[找到[]中的内容
            else
            {
                string str;
                while(mystack.top() != "[")
                {
                    str += mystack.top();
                    mystack.pop();
                }
                //让栈顶的[出栈
                mystack.pop();
                //获取栈顶数字,将字符串str重复
                string num;
                while((!mystack.empty()) && (mystack.top()[0] >= '0' && mystack.top()[0] <= '9'))
                {
                    num += mystack.top()[0];
                    mystack.pop();
                }
                string::iterator itB = num.begin();
                string::iterator itE = num.end();
                reverse(itB,itE);
                for(int i = 1;i <= stoi(num);i++) 
                    mystack.push(str);
            }
        }
        //从栈中将字符串取出,得到结果
        string result;
        while(!mystack.empty())
        {
            result += mystack.top();
            mystack.pop();
        }
        string::iterator itB = result.begin();
        string::iterator itE = result.end();
        reverse(itB,itE);
        return result;
    }
};

4.链表中的下一个更大节点

给出一个以头节点 head 作为第一个节点的链表。链表中的节点分别编号为:node_1, node_2, node_3, ... 。每个节点都可能有下一个更大值(next larger value):对于 node_i,如果其 next_larger(node_i) 是 node_j.val,那么就有 j > i 且  node_j.val > node_i.val,而 j 是可能的选项中最小的那个。如果不存在这样的 j,那么下一个更大值为 0 。返回整数答案数组 answer,其中 answer[i] = next_larger(node_{i+1}) 。

注意:在下面的示例中,诸如 [2,1,5] 这样的输入(不是输出)是链表的序列化表示,其头节点的值为 2,第二个节点值为 1,第三个节点值为 5 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/next-greater-node-in-linked-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

问题分析

用栈解决。将链表存入数组中,方便下标检查,并遍历数组将数组下标进行压栈。

入栈条件:栈为空或者以栈顶元素为下标的数组元素大于入栈元素,将其下标入栈

出栈条件:以栈顶元素为下标的数组元素小于入栈元素,栈顶元素出栈,入栈元素继续与新的栈顶元素比较

代码描述

class Solution {
public:
    vector<int> nextLargerNodes(ListNode* head) {
        //遍历链表,将链表元素存入数组中
        vector<int> list;
        int length = 0;//链表长度
        while(head != NULL)
        {
            list.push_back(head->val);
            length++;
            head = head->next;
        }
        vector<int> ret(length,0);
        stack<int> mystack;
        //遍历链表数组,并压栈
        //入栈条件:栈为空或者以栈顶元素为下标的数组元素大于入栈元素,将其下标入栈
        //出栈条件:以栈顶元素为下标的数组元素小于入栈元素,栈顶元素出栈,入栈元素继续与新的栈顶元素比较
        for(int i = 0;i < length;i++)
        {
            if(mystack.empty() || list[mystack.top()] >= list[i])
                mystack.push(i);
            else
            {
                //将栈顶元素为下标的位置赋值为list[i]
                ret[mystack.top()] = list[i];
                //栈顶元素出栈
                mystack.pop();
                //继续与新的栈顶元素进行比较
                i--;
            }
        }
        return ret;
    }
};

5.下一个更大元素II

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/next-greater-element-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

问题分析

让数组元素两次入栈,第一次入栈将可以找到的元素找到,第二次将剩余元素找到。

代码描述

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        vector<int> ret(nums.size(),-1);
        stack<int> mystack;
        //遍历两次数组
        for(int i = 0;i < nums.size()*2;i++)
        {
            if(mystack.empty() || nums[mystack.top()] >= nums[i%nums.size()])
                mystack.push(i%nums.size());
            else 
            {
                if(ret[mystack.top()] == -1)
                    ret[mystack.top()] = nums[i%nums.size()];
                mystack.pop();
                i--;
            }
        }
        return ret;
    }
};

总结:

1)用栈解决问题时,需要考虑什么时候要进栈,什么时候要出栈,入栈元素跟栈顶元素比较的不同结果的处理方式是什么?

例如:每日温度问题中,入栈条件是栈为空或者栈顶元素大于等于入栈元素;出栈条件是当栈顶元素小于入栈元素;出栈后还需要拿当前元素继续与栈顶元素进行比较,直到该元素满足入栈条件才能入栈。

2)数组中或者字符串的增删改及查找问题适合用栈进行解决

例如:每日温度、行星碰撞等都是对数组进行操作,每日温度需要遍历数组找比当前温度高的温度。

3)用栈解决问题时要考虑栈中保存的应该是数组、字符串的下标还是数组元素。当解决链表问题时还应考虑是否需要将链表中的值保存在数组里。

4)用栈解决问题是以时间换空间的解法,一般情况下时间复杂度和空间复杂度都是O(n)

5)栈解决问题需要考虑清楚栈为空的情况,有时候栈为空表示运行结束,有时候栈为空需要将新的元素入栈。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

疯狂嘚程序猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值