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)栈解决问题需要考虑清楚栈为空的情况,有时候栈为空表示运行结束,有时候栈为空需要将新的元素入栈。