C++学习笔记---013
C++之stack的应用及模拟实现
前言:
前面篇章学习了C++对于list容器的基本使用以及常用接口的认识,接下来继续学习,C++的stack的模拟实现等知识。
/知识点汇总/
1、stack的简单介绍
在C++中,stack是一种标准模板库(STL)中的容器适配器,它提供了后进先出(LIFO)的数据结构。
换句话说,最后添加到stack中的元素将是第一个被移除的元素。
这种数据结构非常适用于那些需要按照逆序处理元素的场景。
以下是stack的一些基本特性和操作:
1.基本特性:
LIFO(后进先出)原则。
栈顶元素是最后一个插入的元素,也是第一个被移除的元素。
栈底元素是第一个插入的元素,但通常是最后一个被移除的元素(除非整个栈被清空)。
2.常用操作:
push(element): 将一个元素添加到栈顶。
pop(): 移除栈顶的元素。注意,这个操作不返回被移除的元素,也不检查栈是否为空。在调用pop()之前,应确保栈不为空,否则可能会导致未定义行为。
top(): 返回栈顶元素的引用,但不移除它。同样,调用此操作之前应确保栈不为空。
empty(): 检查栈是否为空。如果为空,返回true;否则返回false。
size(): 返回栈中元素的数量。
2、stack的简单接口应用
void test_stack()
{
stack<int> st;
st.push(1);
st.push(2);
st.push(3);
st.push(4);
st.push(5);
while (!st.empty())
{
cout << st.top() << " ";
st.pop();
}
cout << endl;
}
3、stack的模拟实现
3.1、stack的结构一般的构建
一般方法的构建:
template<class T>
class stack
{
private:
T* _a;
int _top;
int _capacity;
};
3.2、stack的适配器模式构建
适配器模式 – 本质就是转换
stack<int, vector> st1;
stack<int, list> st2;
template<class T,class Container>
泛型编程,兼容多种类型的栈,满足链表list和顺序表vector…
template<class T, class Container = vector<T>>
class stack
{
private:
Container _con;
};
3.3、stack的主要接口函数
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_back();
}
size_t size()
{
return _con.size();
}
bool empty()
{
return _con.empty();
}
const T& top()
{
return _con.back();
}
4、stack的模拟实现完整代码
namespace bit
{
//一般方法的构建
/*
template<class T>
class stack
{
private:
T* _a;
int _top;
int _capacity;
};
*/
//适配器模式 -- 本质就是转换
//stack<int, vector<int>> st1;
//stack<int, list<int>> st2;
//template<class T,class Container>//泛型编程,兼容多种类型的栈,满足链表list和顺序表vector...
template<class T, class Container = vector<T>>
class stack
{
public:
void push(const T& x)
{
_con.push_back(x);
}
void pop()
{
_con.pop_back();
}
size_t size()
{
return _con. size();
}
bool empty()
{
return _con.empty();
}
const T& top()
{
return _con.back();
}
private:
Container _con;
};
}
void test_stack1()
{
//bit::stack<int, list<int>> st;
//bit::stack<int, vector<int>> st;
//模板
bit::stack<int> st; //隐式参数,解决常用模板的参数问题
st.push(1);
st.push(2);
st.push(3);
while (!st.empty())
{
cout << st.top() << " ";
st.pop();
}
cout << endl;
}
5、stack巩固练习题
5.1、最小栈
最小栈:在常数级时间内检索最小元素的栈
思路:
两个栈,一个存放原始数据,一个存放当前最小值(不断更新栈顶元素min)
class MinStack
{
public:
MinStack()
{}
//两个情况进值:
//1.最开始空栈
//2.更新栈顶min小值
void push(int val)
{
_st.push(val);
if (_minst.empty() || val <= _minst.top())
{
_minst.push(val);
}
}
void pop()
{
if (_minst.top() == _st.top())
{
_minst.pop();
}
_st.pop();
}
int top()
{
return _st.top();
}
int getMin()
{
return _minst.top();
}
stack<int> _st;
stack<int> _minst;
};
int main()
{
MinStack st;
st.push(2);
st.push(1);
st.push(3);
st.push(4);
st.push(0);
st.pop();
cout << st.top() << endl;
cout << st.getMin() << endl;
return 0;
}
5.2、栈的匹配(栈的压入和弹出序列)
满足栈的压入与出栈序列合理。
意思就是出栈序列,可满足栈的入栈序列,即”先进后出“。
思路:
1.先把入栈序列入栈
2.栈顶元素和出栈序列是否匹配
a、如果匹配就持续出数据,直到栈为空为止
b、如果不匹配,继续入栈
3.结束标志:a、入栈序列走完了 b、栈走完了,也不匹配,不合法的序列
class Solution
{
public:
bool IsPopOrder(vector<int>& pushV, vector<int>& popV)
{
int pushi = 0, popi = 0;
stack<int> st;
while (pushi < pushV.size())
{
st.push(pushV[pushi++]);
while (!st.empty() && st.top() == popV[popi])
{
++popi;
st.pop();
}
}
return st.empty();
}
};
int main()
{
Solution sol;
vector<int> pushV = { 1, 2, 3, 4, 5 };
//vector<int> popV = { 5, 4, 3, 2, 1 };
vector<int> popV = { 4, 5, 3, 2, 1 };
//vector<int> popV = { 4, 3, 5, 1, 2 };
// 检查出栈序列是否是可能的
bool isPossiblePopOrder = sol.IsPopOrder(pushV, popV);
// 打印结果
cout << "Is the pop order possible? " << (isPossiblePopOrder ? "Yes" : "No") << endl;
return 0;
}
5.3、逆波兰表达式求值 (后缀表达式)
操作数在操作符之后的表达式,比如:abcd-*+ — 遇见运算符就开始计算
思路:
利用栈,栈处理操作数和运算符的顺序关系优先级,遇到操作数就入栈,遇到操作数就出栈操作数,运算结果继续入栈,再继续下一个操作数。
class Solution
{
public:
int evalRPN(vector<string>& tokens)
{
stack<int> st;
//写法2:set
set<string> s = { "+","-","*","/" };
for (auto& str : tokens)
{
//1.操作数入栈,遇到运算符运算
//写法1:穷举
//if (str == "+" || "-" == str || "*" == str || "/" == str)
//写法2:set -- 容器,搜索树
if(s.find(str) != s.end())
{
//是操作符,根据栈的性质
//先取的是右操作数,再是左操作数
int right = st.top();
st.pop();
int left = st.top();
st.pop();
//运算后的结果入栈
switch (str[0])//str[0] --- char整型与case参数匹配
{
case '+':
st.push(left + right);//操作数顺序还原
break;
case '-':
st.push(left - right);
break;
case '*':
st.push(left * right);
break;
case '/':
st.push(left / right);
break;
}
}
else
{
st.push(stoi(str));
}
}
return st.top();
}
};
int main()
{
Solution sol;
// 逆波兰表示法的表达式
vector<string> tokens = { "2", "1", "+", "3", "*" };
// 计算表达式的值
int result = sol.evalRPN(tokens);
// 打印结果
cout << "The result of the expression is: " << result << endl;
return 0;
}
5.4、两个栈实现队列
实现用两个栈模拟实现队列的”先进先出“。
思路:
利用两个栈相互倒,从而满足队列的性质
即:一个push栈进数据,push栈倒数据到一个pop栈,让pop栈出数据。
class MyQueue {
public:
MyQueue()
{}
void push(int x)
{
_pushst.push(x);
}
int pop()
{
int retpop = peek();
_popst.pop();
return retpop;
}
int peek()
{
if (_popst.empty())
{
//倒数据
while (!_pushst.empty())
{
_popst.push(_pushst.top());
_pushst.pop();
}
}
return _popst.top();
}
bool empty()
{
return _pushst.empty() && _popst.empty();
}
private:
stack<int> _pushst;
stack<int> _popst;
};
int main()
{
MyQueue qt;
qt.push(1);
qt.push(2);
qt.push(3);
qt.push(4);
qt.push(5);
while (!qt.empty())
{
printf("%d ", qt.peek());
qt.pop();
}
return 0;
}