目录
栈:
队列:
1:用栈实现队列
2:用队列实现栈
3:有效的括号
栈!匹配的神!消消乐的神!
4:删除字符串中的所有相邻重复项
5:逆波兰表达式求值(离大谱)
6:滑动窗口最大值(困难单调队列)
二叉树
二叉树的遍历:
二叉树的递归遍历方式:
二叉树的迭代遍历方式:栈
特性:先进后出
C++实现:
#include <stack> using namespace std; stack<type>st;
特性:先进先出
C++实现:
#include <queue> using namespace std; queue<int> que;
具体的STL库函数就不介绍了,我推荐一位博主大佬^ ^的文章
讲原理肯定是用C来实现的^ ^ ,但这里是题解^ ^,所以用的是C++
【两万字精编】蓝桥杯算法竞赛系列第0章——蓝桥必考点及标准模板库STL(下)_安然无虞的博客-CSDN博客https://bit-runout.blog.csdn.net/article/details/121383654
思路解析:
重点:请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作
初始化:
stack<int> stIn;//用来放 stack<int> stOut;//用来输出
push():
void push(int x) { stIn.push(x);//在输入栈里插入元素即可 }
pop():
int pop() { if(stOut.empty())//如果输出的栈为空 { while(!stIn.empty())//输入的栈为非空,也就是输入栈里还有元素时 { stOut.push(stIn.top());//将输入栈的栈顶的元素插入输出栈里 stIn.pop();//删除输入栈的栈顶(注意此时的top会--) } } int result=stOut.top();//将栈顶元素存入result; stOut.pop();//删除输出栈的栈顶元素 return result;//返回被删除的元素 }
解释:由于队列是先进先出,所以被删除的是在队列最前面的数
而栈只能删除栈顶的元素,画个图,图解一下
通过两个输入输出栈的转换,我们就实现了队列的头删
peek():
int peek() { int res=this->pop(); stOut.push(res); return res; }
解析:很简单,因为我们已经用pop找到了头元素,接收这个头元素并返回即可
empty():
bool empty() { return stIn.empty()&&stOut.empty(); }
解释:如果输入和输出栈均为空,那么就返回true,反之返回false;
完整代码:
class MyQueue { public: stack<int> stIn; stack<int> stOut; MyQueue() { } void push(int x) { stIn.push(x); } int pop() { if(stOut.empty()) { while(!stIn.empty()) { stOut.push(stIn.top()); stIn.pop(); } } int result=stOut.top(); stOut.pop(); return result; } int peek() { int res=this->pop(); stOut.push(res); return res; } bool empty() { return stIn.empty()&&stOut.empty(); } };
思路解析:
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(
push
、top
、pop
和empty
)。
核心思路:
由于栈的特点是后入先出,所以在队列1中,我们要实现后面的元素先出来,我们即可让前面的元素先存到另外一个队列2,然后此时在队列1中,后面的元素即可变为头元素,按照性质直接出来
class MyStack { public: queue<int>que1; queue<int>que2; MyStack() { } void push(int x) { que1.push(x); } int pop() { int size=que1.size();//获取长度 size--;//目的是为让que1只剩一个元素 while(size--)//将que1的前元素放入que2里 { que2.push(que1.front()); que1.pop(); } int result=que1.front();//删除的元素就是que1仅剩的那个元素 que1.pop(); que1=que2;//将que2的赋值给que1,重新初始化 while(!que2.empty())//将que2里的元素删空 { que2.pop(); } return result;//返回在que1里被删除的元素 } int top() { return que1.back();//队列可以直接查找到队列的尾 } bool empty() { return que1.empty();//队列2在不进行删除时,都是为空的 } }; /** * Your MyStack object will be instantiated and called as such: * MyStack* obj = new MyStack(); * obj->push(x); * int param_2 = obj->pop(); * int param_3 = obj->top(); * bool param_4 = obj->empty(); */
思路解析:
(1)遍历字符串
(2)如果遇到左括号,则插入与它相对应的右括号
(3)如果栈顶元素与遍历到的右括号相等,那么就删除栈顶元素
判断:如果遍历还没有结束,栈就为空,那么返回false;
如果遍历结束,栈为空,返回true,反之栈不为空,返回fasle;
代码实现:
class Solution { public: bool isValid(string s) { stack<int> st; for (int i = 0; i < s.size(); i++) { if (s[i] == '(') st.push(')'); else if (s[i] == '{') st.push('}'); else if (s[i] == '[') st.push(']'); // 第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号 return false // 第二种情况:遍历字符串匹配的过程中,发现栈里没有我们要匹配的字符。所以return false else if (st.empty() || st.top() != s[i]) return false; else if(st.top()==s[i]) st.pop(); // st.top() 与 s[i]相等,栈弹出元素 } // 第一种情况:此时我们已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false,否则就return true return st.empty(); } };
思路解析:
本题与上题相同,均是属于消消乐类型
(1)遍历字符串
(2)如果与栈顶元素相等则删除栈顶元素
反之,插入栈里
(3)此时栈里的元素就是没办法继续消消乐的元素,由于只能逆序弹出
所以我们在返回时需要再将字符串逆序
代码实现:
class Solution { public: string removeDuplicates(string S) { stack<char> st; for (char s : S) { if (st.empty() || s != st.top()) { st.push(s); } else { st.pop(); // s 与 st.top()相等的情况 } } string result = ""; while (!st.empty()) { // 将栈中元素放到result字符串汇总 result += st.top();//将栈中元素逆序加入字符串中 st.pop(); } reverse (result.begin(), result.end()); // 此时字符串需要反转一下 return result; } };
更巧的方法:直接将字符串作为栈
class Solution { public: string removeDuplicates(string S) { string result; for(char s : S) { if(result.empty() || result.back() != s) { result.push_back(s); } else { result.pop_back();//如果遇到相同的元素,则在字符串中删除那个元素 } } return result; } };
相信看到这个样例你是有点懵的
再看几个样例
直接给我整自闭,题目都看不明白,其实这里题目就是用了栈的思考方式
可以看这个动图,就明白了
代码随想录https://www.programmercarl.com/0150.%E9%80%86%E6%B3%A2%E5%85%B0%E8%A1%A8%E8%BE%BE%E5%BC%8F%E6%B1%82%E5%80%BC.html思路解析:
(1)遍历字符串
(2)如果遍历到的元素为加减乘除,那么就让它的前两个数字进行加减乘除
如果遍历到的元素不为加减乘除,那么就让该字符转为数字
(3)最后栈里只剩一个元素,弹出这个元素,返回即可
class Solution { public: int evalRPN(vector<string>& tokens) { stack<int>st; for(int i=0;i<tokens.size();i++) { if (tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/") { int num1=st.top(); st.pop(); int num2=st.top(); st.pop(); if (tokens[i] == "+") st.push(num2 + num1); if (tokens[i] == "-") st.push(num2 - num1); if (tokens[i] == "*") st.push(num2 * num1); if (tokens[i] == "/") st.push(num2 / num1); } else { st.push(stoi(tokens[i])); } } int result=st.top(); st.pop(); return result; } };
思路解析:
设计单调队列的时候,pop,和push操作要保持如下规则:
- pop(value):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
- push(value):如果push的元素value大于入口元素的数值,那么就将队列入口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止
- 单调队列设计好后,滑动窗口的简单遍历即可
- 代码随想录代码随想录PDF,代码随想录百度网盘,代码随想录知识星球,代码随想录八股文PDF,代码随想录刷题路线,代码随想录知识星球八股文https://www.programmercarl.com/0239.%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E6%9C%80%E5%A4%A7%E5%80%BC.html(动图展示)
核心:在k个数范围内的单调队列(只有一个数也算单调),那么只要取出最开头的元素,那么它就一定是k个数内最大的数;至于删除:只有在原数组中遍历到这个这个位置,并且处于首端,那么才会被删除(需要慢慢体会)
class Solution { private: class MyQueue { //单调队列(从大到小) public: deque<int> que; // 使用deque来实现单调队列 // 每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。 // 同时pop之前判断队列当前是否为空。 void pop(int value) { if (!que.empty() && value == que.front()) { que.pop_front(); } } // 如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。 // 这样就保持了队列里的数值是单调从大到小的了。 void push(int value) { while (!que.empty() && value > que.back()) { que.pop_back(); } que.push_back(value); } // 查询当前队列里的最大值 直接返回队列前端也就是front就可以了。 int front() { return que.front(); } }; public: vector<int> maxSlidingWindow(vector<int>& nums, int k) { MyQueue que; vector<int> result; for (int i = 0; i < k; i++) { // 先将前k的元素放进队列 que.push(nums[i]); } result.push_back(que.front()); // result 记录前k的元素的最大值 for (int i = k; i < nums.size(); i++) { que.pop(nums[i - k]); // 滑动窗口移除最前面元素 que.push(nums[i]); // 滑动窗口前加入最后面的元素 result.push_back(que.front()); // 记录对应的最大值 } return result; } };
二叉树的实现:
struct TreeNode { int val; TreeNode* left; TreeNode* right; };
前序遍历:中左右
class Solution { public: void traversal(TreeNode* cur, vector<int>& vec) { if (cur == NULL) return; vec.push_back(cur->val); // 中 traversal(cur->left, vec); // 左 traversal(cur->right, vec); // 右 } vector<int> preorderTraversal(TreeNode* root) { vector<int> result; traversal(root, result); return result; } };
中序遍历:左中右
void traversal(TreeNode* cur, vector<int>& vec) { if (cur == NULL) return; traversal(cur->left, vec); // 左 vec.push_back(cur->val); // 中 traversal(cur->right, vec); // 右 } vector<int> preorderTraversal(TreeNode* root) { vector<int> result; traversal(root, result); return result; } };
后序遍历:左右中
void traversal(TreeNode* cur, vector<int>& vec) { if (cur == NULL) return; traversal(cur->left, vec); // 左 traversal(cur->right, vec); // 右 vec.push_back(cur->val); // 中 } vector<int> preorderTraversal(TreeNode* root) { vector<int> result; traversal(root, result); return result; } };
前序遍历:中左右
解释:注意因为,出栈和进栈的顺序相反,所以是先进右再进左
struct TreeNode { int val; TreeNode* left; TreeNode* right; }; //前序遍历 vector<int> preorderTraversal(TreeNode* root) { stack<TreeNode*>st; vector<int> result; if (root == NULL) return result; st.push(root);//先放中间结点 while (!st.empty())//当栈非空则继续 { TreeNode* node = st.top();//将*node赋值为栈顶元素 st.pop();//删除栈顶元素 result.push_back(node->val);//将栈顶元素指向的val尾插入result; if (node->right) st.push(node->right);//如果结点的右边不为空,则入栈 if (node->left) st.push(node->left);//如果结点的左边不为空,则入栈 } return result; }
后序遍历:左右中
解释因为:先序遍历为:中左右 后序遍历为:左右中
那么我们可以在先序遍历中变为:中右左,然后在把result数组反转即可得到正确的后序遍历
struct TreeNode { int val; TreeNode* left; TreeNode* right; }; vector<int> postorderTraversal(TreeNode* root) { stack<TreeNode*>st; vector<int> result; if (root == NULL) return result; st.push(root);//节点入栈 while (!st.empty()) { TreeNode* node = st.top(); st.pop(); result.push_back(node->val); } }
中序遍历:
解释:一开始就从节点的左边开始找,边找边存入栈,直到遇到NULL,开始出栈并尾插入result数组
struct TreeNode { int val; TreeNode* left; TreeNode* right; }; vector<int> inorderTraversal(TreeNode* root) { vector<int>result; stack<TreeNode*>st; TreeNode* cur = root; while (cur != NULL || !st.empty()) { if (cur != NULL)//不为空 { st.push(cur);//入栈 cur = cur->left;//cur继续找最左边的节点 } else { cur = st.top();//如果为空,说明该上一个节点为最左边的 st.pop();//删除栈顶 result.push_back(cur->val);//将原栈顶指向的val尾插入result cur = cur->right;//访问右节点 } } return result; }