目录
- 什么是栈?
- 题目类型
- 顺序栈
- 实践题目
- [155. 最小栈](https://leetcode-cn.com/problems/min-stack/)
- [42. 接雨水](https://leetcode-cn.com/problems/trapping-rain-water/)
- [71. 简化路径](https://leetcode-cn.com/problems/simplify-path/)
- [496. 下一个更大元素 I](https://leetcode-cn.com/problems/next-greater-element-i/)
- [682. 棒球比赛](https://leetcode-cn.com/problems/baseball-game/)
- [1021. 删除最外层的括号](https://leetcode-cn.com/problems/remove-outermost-parentheses/)
- [341. 扁平化嵌套列表迭代器](https://leetcode-cn.com/problems/flatten-nested-list-iterator/)
- [331. 验证二叉树的前序序列化](https://leetcode-cn.com/problems/verify-preorder-serialization-of-a-binary-tree/)
- [1081. 不同字符的最小子序列](https://leetcode-cn.com/problems/smallest-subsequence-of-distinct-characters/)
什么是栈?
是一个运算受限制的线性表,只能在表尾进行插入和删除,也就是先进后出,后进先出。(新手建议搜一本算法图解,讲的很明白)
题目类型
原理学习:155、71、682、341
单调栈:42、496、1081
辅助栈(只是用来计数的一个工具):1021、331
个人感觉没有新类型的,基本上就不重复记录题目了。
顺序栈
理解的话百度百科就可以,俺也是记笔记,怕误导你们。链接
顺序栈的适用情况(算法太多总结一下):
有明显的排序现象,或者是找左边啊右边啊第一个最大(最小)的情况。
实践题目
155. 最小栈
struct Node{
int val;
int min;
Node *next;
};
//建立结构体,写入题目要求的功能
class MinStack {
private:
Node* head;
public:
MinStack() {
head = nullptr;
}
//构造函数
void push(int x) {
if(head != NULL){
int temp = (head -> min > x)? x : head -> min;
Node *node = new Node();
node -> min = temp;
node -> val = x;
node -> next = head;
head = node;
}//这里使用头插
else{
head = new Node();
head -> val = x;
head -> min = x;
head -> next = nullptr;
}
}
void pop() {
head = head ->next;
}
int top() {
return head -> val;
}
int getMin() {
return head -> min;
}
};
42. 接雨水
这个用单调递减栈:
稍微讲一下单调递减栈和单调递增栈:
(以单调递减栈为例)
1.若数据小于栈顶元素,则入栈。
2.如果数据比栈顶元素大,那么栈顶元素出栈,一直到数据比栈顶元素小为止。
打个比方:4312
4,3,1正常入栈
到2,和1比,1出栈,和3比,比3小,就出结果啦。
这道题我们只需要求有水滴的地方的面积,所以我们栈里存的数据是数组的下标(当然有其他解法,但是我这里用栈)
class Solution {
public:
int trap(vector<int>& height) {
stack<int> num;
int sum = 0;
for(int i = 0;i < height.size();i++){
while(!num.empty() && height[num.top()] < height[i]){
int val = height[num.top()];
num.pop();
if(num.empty()) break;
int l = i - num.top() - 1;
int h = min(height[i],height[num.top()]) - val;
sum += l * h;
}
num.push(i);
}
return sum;
}
};
71. 简化路径
读完题目和例子,我得到的信息:
1、如果栈顶是/下一个要入栈的数据也是/那么栈顶出栈,下一个数据正常入栈。
2、如果栈顶是/(不排除例子乱输入a…这种情况)下一个要入栈的数据是…则数据全部出栈,下一个数据入栈。
3、如果栈顶是/,下一个要入栈数据是.那么栈顶出栈,而且继续出栈到下一个栈顶为/
4、在程序运行最后,判断栈顶是否是/,如果是就出栈。
怎么说呢,我们只需要判断两个/之间的东西是什么,再做相应操作就好了。
class Solution {
public:
string simplifyPath(string path) {
path += '/';
stack<string> stk;
string str;
for(char m : path){
if(m == '/'){
//如果str为.那么直接清除str即可
//如果str为..那么stk把上一个数据给出栈(这里有特殊情况/../需要判断是否为空栈)
//其他情况就把数据读进去,注意我这么写'/'这个字符是不会读进去的
if(str == ".." && !stk.empty()){
stk.pop();
}
else if(str != "." && !str.empty() && str != ".."){
stk.push(str);
}
str.clear();
}
else{
str.push_back(m);
}
}
string result;
if(stk.empty()) return "/";
while(!stk.empty()){
result = "/" + stk.top() + result;
stk.pop();
}
return result;
}
};
496. 下一个更大元素 I
这个题用递减数列,把小数据存进去找右边第一个大的数据,再把这个关系存进去(记得把栈顶出栈),我用hashmap进行关系建立,最后查结果,查不到就返回-1。
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
stack<int> num;
vector<int> answer;
map<int, int> s1;
for(auto a:nums2){
while(!num.empty() && num.top() < a){
s1[num.top()] = a;
num.pop();
}
num.push(a);
}
for(auto a:nums1){
if(s1.count(a) == 0) answer.push_back(-1);
else answer.push_back(s1[a]);
}
return answer;
}
};
682. 棒球比赛
这个题也是给练手题,本来以为用asl码,但是俺也不知道棒球比赛能有60分?
class Solution {
public:
int calPoints(vector<string>& ops) {
stack<int> num;
int sum = 0;
for(auto a:ops){
if(a[0] == '+'){
int t = num.top();
num.pop();
int t1 = t + num.top();
num.push(t);
num.push(t1);
}
else if(a[0] == 'D'){
int t = num.top() + num.top();
num.push(t);
}
else if(a[0] == 'C'){
num.pop();
}
else num.push(stoi(a));//大家可以记一下这个函数
}
while(!num.empty()){
sum += num.top();
num.pop();
}
return sum;
}
};
1021. 删除最外层的括号
我开始是寻找两个栈空之间的距离,把这两个之间的东西加起来,但是看了一下大佬的写的= =自愧不如,参考的力扣用户:ping-ze-ping-ze-ping-ping-ze的代码。
class Solution {
public:
string removeOuterParentheses(string S) {
stack<char> a;
string b("");
for(int i = 0;i < S.length();i++){
if(S[i] == ')') a.pop();
if(!a.empty() ) b += S[i];
if(S[i] == '(') a.push(S[i]);
}
return b;
}
}
341. 扁平化嵌套列表迭代器
难度中等题里面适合练手(俺开始没看懂题意,写的好烦啊)。
class NestedIterator {
private:
stack<int> a;
stack<int> b;
void add(vector<NestedInteger> nested){
for(auto nestedi : nested)
if(nestedi.isInteger()) a.push(nestedi.getInteger());
else add(nestedi.getList());
}
public:
NestedIterator(vector<NestedInteger> &nestedList) {
add(nestedList);
while(!a.empty()){
b.push(a.top());
a.pop();
}
}
int next() {
int ai = b.top();
b.pop();
return ai;
}
bool hasNext() {
return !b.empty();
}
};
331. 验证二叉树的前序序列化
这是一棵二叉树 = =反正我的印象就是:空节点数=非空节点数 + 1,建议几个关于树的定理可以记一下。
然后#就是空,数就是非空,#比数多1个,那么如果是数就进栈,如果是#就出栈,那么当字符是#的时候而且#是字符串最后一位看栈是否为空,若为空那么就是验证成功,非空就是验证失败。
然后俺写出来就遇见两位的数字19这种,然后这是个string,难搞= =借鉴了一下别人的做法,他每次找到数,就往下找到下一个”,“数有很多”,“只有一个啊不得不说这个想法真厉害。
class Solution {
private:
stack<char> a;
public:
bool isValidSerialization(string preorder) {
if (preorder.empty()) return false;
for(int i = 0;i < preorder.length();i++){
if(preorder[i] == '#'){
if(a.empty()) return i == preorder.size() - 1;
else {
a.pop();
i++;
}
}
else{
while (i < preorder.size() && preorder[i] != ',') i++;
a.push(preorder[i]);
}
}
return false;
}
};
1081. 不同字符的最小子序列
这个问题原理很简单,就是一个单调栈(单调递增),如果栈内已经存在数据就吧这个数据给pass掉,如果数据比栈顶小,那么就删栈顶数据直到栈顶数据小于数据为止。因为C++的库函数实在感觉少,就用vector来代替,其实大家用go啊python啊可以直接调库。
class Solution {
public:
bool get_num(int i,string s,char si){
for(int j = i + 1;j<s.length();j++) if(s[j] == si) return true;
return false;
}
string smallestSubsequence(string s) {
string str("");
vector<char> already;
int i = -1;
for(auto si :s){
i++;
if(count(already.begin(),already.end(),si) > 0) continue;
while(!already.empty() && already.back() > si && get_num(i,s,already.back()) == true) already.pop_back();
already.push_back(si);
}
while(!already.empty()){
str = already.back() + str;
already.pop_back();
}
return str;
}
};