C++数据结构之栈,队列,树(力扣题解)
作者:互联网
目录
栈:
特性:先进后出
C++实现:
#include <stack>
using namespace std;
stack<type>st;
队列:
特性:先进先出
C++实现:
#include <queue>
using namespace std;
queue<int> que;
具体的STL库函数就不介绍了,我推荐一位博主大佬^ ^的文章
讲原理肯定是用C来实现的^ ^ ,但这里是题解^ ^,所以用的是C++
1:用栈实现队列
思路解析:
重点:请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作
初始化:
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();
}
};
2:用队列实现栈
思路解析:
请你仅使用两个队列实现一个后入先出(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();
*/
3:有效的括号
思路解析:
栈!匹配的神!消消乐的神!
(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();
}
};
4:删除字符串中的所有相邻重复项
思路解析:
本题与上题相同,均是属于消消乐类型
(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;
}
};
5:逆波兰表达式求值(离大谱)
相信看到这个样例你是有点懵的
再看几个样例
直接给我整自闭,题目都看不明白,其实这里题目就是用了栈的思考方式
可以看这个动图,就明白了
(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;
}
};
6:滑动窗口最大值(困难单调队列)
思路解析:
设计单调队列的时候,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;
}
标签:遍历,队列,题解,之栈,C++,st,result,push,元素 来源: https://blog.csdn.net/lihua777/article/details/122774831