目录
一 、 栈的应用
1 . 栈在括号匹配中的应用
1-1 顺序栈实现
#include <iostream>
using namespace std;
#define MaxSize 50
typedef char ElemType;
typedef struct {
ElemType data[MaxSize];//最大容量
int top;//栈顶指针
} SqStack;
//初始化栈
void InitStack(SqStack &S);
//判断栈空
bool StackEmpty(SqStack S);
//新元素入栈
bool Push(SqStack &S, ElemType x);
//元素出栈
bool Pop(SqStack &S, ElemType &x);
//读取栈顶元素
bool GetPop(SqStack &S, ElemType &x);
//括号匹配
bool bracketCheck(const char str[], int length) {
SqStack S;
InitStack(S);//初始化一个栈
for (int i = 0; i < length; i++) {
if(str[i] == '(' || str[i] == '[' || str[i] == '{') { //扫描到左括号 压入栈中
Push(S, str[i]);
} else {
if(StackEmpty(S)) { //扫描到右括号且当前栈空
return false; //匹配失败
}
char topElem;
Pop(S, topElem);
if(str[i] == ')' && topElem != '(') {
return false;
}
if(str[i] == ']' && topElem != '[') {
return false;
}
if(str[i] == '}' && topElem != '{') {
return false;
}
}
}
return StackEmpty(S);//检索结束后 栈空 则说明匹配成功
}
//初始化栈
void InitStack(SqStack &S) {
S.top = -1;//初始化栈顶指针
}
//判断栈空
bool StackEmpty(SqStack S) {
if(S.top == -1) {
return true;
} else {
return false;
}
}
//新元素入栈
bool Push(SqStack &S, ElemType x) {
if(S.top == MaxSize - 1) { //栈满 报错
return false;
}
S.top += 1;
S.data[S.top] = x;
// S.data[++S.top] = x;
return true;
}
//元素出栈
bool Pop(SqStack &S, ElemType &x) {
if(S.top == -1) { //栈空
return false;
}
x = S.data[S.top];
S.top -= 1;
// x = S.data[S.top--];
return true;
}
//读取栈顶元素
bool GetPop(SqStack &S, ElemType &x) {
if(S.top == -1) { //栈空
return false;
}
x = S.data[S.top];
return true;
}
int main() {
if(bracketCheck("(()){}[[]]{{}}{()}", 18)) { //匹配成功
cout << "Match Successfully!" << endl;
} else {
cout << "Match Failed!" << endl;
}
if(bracketCheck("{()[][[]]}{{})", 14)) { //匹配失败
cout << "Match Successfully!" << endl;
} else {
cout << "Match Failed!" << endl;
}
return 0;
}
1-2 代码运行结果![](https://i-blog.csdnimg.cn/blog_migrate/b3b676daf71fd16a66fcbab0719974b7.png)
2-1 链栈实现
#include <iostream>
using namespace std;
//带头结点
typedef char ElemType;
typedef struct LinkNode {
ElemType data;//数据域
struct LinkNode *next;//指针域
} *LiStack, LinkNode; //栈类型定义
//初始化栈
bool InitStack(LiStack &S);
//判断栈空
bool StackEmpty(LiStack S);
//新元素入栈
bool Push(LiStack &S, ElemType x);
//元素出栈
bool Pop(LiStack &S, ElemType &x);
//读取栈顶元素
bool GetPop(LiStack &S, ElemType &x);
//括号匹配
bool bracketCheck(const char str[], int length) {
LiStack S;
InitStack(S);//初始化一个栈
for (int i = 0; i < length; i++) {
if(str[i] == '(' || str[i] == '[' || str[i] == '{') { //扫描到左括号 压入栈中
Push(S, str[i]);
} else {
if(StackEmpty(S)) { //扫描到右括号且当前栈空
return false; //匹配失败
}
char topElem;
Pop(S, topElem);
if(str[i] == ')' && topElem != '(') {
return false;
}
if(str[i] == ']' && topElem != '[') {
return false;
}
if(str[i] == '}' && topElem != '{') {
return false;
}
}
}
return StackEmpty(S);//检索结束后 栈空 则说明匹配成功
}
//初始化链栈(带头结点)
bool InitStack(LiStack &S) {
S = (LinkNode *)malloc(sizeof(LinkNode));
if(!S) { //申请失败
return false;
}
S->next = NULL;
return true;
}
//判断链栈是否为空
bool StackEmpty(LiStack S) {
if(S->next == NULL) {
return true;
}
return false;
}
//入栈操作
bool Push(LiStack &S, ElemType x) {
LinkNode *p = (LinkNode *)malloc(sizeof(LinkNode));
if(!p) { //申请失败
return false;
}
p->data = x;
p->next = S->next;
S->next = p;
return true;
}
//弹栈操作
bool Pop(LiStack &S, ElemType &x) {
if(StackEmpty(S)) { //空栈
return false;
}
LinkNode *p = S->next;
x = p->data;
S->next = p->next;
free(p);
return true;
}
//读取栈顶元素
bool GetTop(LiStack S, ElemType &x) {
if(StackEmpty(S)) { //空栈
return false;
}
x = S->next->data;
return true;
}
//销毁链栈
bool DestoryStack(LiStack &S) {
while(S->next != NULL) {
LinkNode *p = S->next;
S->next = p->next;
free(p);
}
free(S);
return true;
}
int main() {
if(bracketCheck("(()){}[[]]{{}}{()}", 18)) { //匹配成功
cout << "Match Successfully!" << endl;
} else {
cout << "Match Failed!" << endl;
}
if(bracketCheck("{()[][[]]}{{})", 14)) { //匹配失败
cout << "Match Successfully!" << endl;
} else {
cout << "Match Failed!" << endl;
}
return 0;
}
2-2 代码运行结果![](https://i-blog.csdnimg.cn/blog_migrate/c9fc8f257ccfe4009b8f816f012f9031.png)
2 . 栈在表达式求值中的应用
栈的一个重点基础运用就是,四则表达式的求值,这里面困难在于,乘除在加减后面,却要先运算,加入括号后,就变的更加复杂。波兰逻辑学家想到了一种不需 要括号的后缀表达法,我们也把它称为逆波兰(Reverse Polish Notation, RPN)表示。Q:对于9+(3-1)*3+10/2,如果用后缀表达式,则为:9 3 1 - 3 * +10 2 / +。我们先来看看用后缀表达式计算机如何求其值。后缀表达式计算过程 规则:从左到右遍历表达式的每个数字和符号,遇到是数字就进栈,遇到是符号,就将处于栈顶两个数字出栈,进行运算,运算结果进栈,一直到最终获得结果 计算过程:初始化一个空栈,前三个都为数字,于是压入,9 3 1,遇到 - 号,被减数3,减数1出栈进行计算,结果为2,此时压入2,接着3入栈,又遇到 * 号,2,3出栈计算,结果为6,6入栈,下面是+ 号,9,6出栈,相加,结果为15,入栈。10,2入栈,遇到 / 号,将两数出栈计算,结果为5,入栈,最后是+ 号,栈中15,5计算,结果为20,而20就是答案。果然,后缀表达式可以轻松解决问题。那么我们需要得到后缀表达式。算法思想
//中缀表达式转后缀表达式 初始化一个栈 用于保存暂时还不能确定运算顺序的运算 从左到右处理各个元素 直到末尾 可能遇到三种情况 //1. 遇到 操作数 直接加入后缀表达式 //2. 遇到 界限符 遇到'('直接入栈;遇到')'则依次弹出栈内运算符并加入后缀表达式直到弹出'('为止 注意:')'不加入后缀表达式 //3. 遇到 运算符 依次弹出栈中优先级高于或等于当前运算符的所有运算符 并加入后缀表达式 若碰到'('或栈空则停止 之后再把当前运算符入栈 按上述方法处理完所有字符后 将栈中剩余运算符依次弹出 并加入后缀表达式 //用栈实现后缀表达式的计算: ①从左往右扫描下一个元素,直到处理完所有元素 ②若扫描到操作数则压入栈,并回到①;否则执行③ ③若扫描到运算符,则弹出两个栈顶元素,执行相应运算,运算结果压回栈顶,回到①//中缀表达式的计算(用栈实现) //初始化两个栈 操作数栈 运算符栈 //若扫描到操作数 压入操作数栈 //若扫描到操作符 则按照"中缀转后缀"相同的逻辑压入运算符栈(期间也会弹出运算符)每当弹出一个运算符时 就需要再弹出两个操作数栈的栈顶元素并执行相应运算运算结果再压入操作数栈依旧拿上面的 9+(3-1)*3+10/2 分析:遍历,9输出,+ 栈中为空,入栈,左括号入栈3输出,- 不高于 + ,入栈,1 输出,当前输出9 3 1,栈中+ ( -右括号,- ,(出栈,遇到左括号,输出为9 3 1 -, 栈中+符号* 优先级大于+ ,入栈,3输出,当前输出为9 3 1 - 3,栈中+ *遇到+,优先级小于,等于+,出栈,+出栈,+入栈,栈中+,输出为9 3 1 - 3 * +10输出,/ 优先级大于+,/ 入栈,当前输出为9 3 1 - 3 * + 10 ,栈中+ / 2 输出,栈中输出,得到最终结果。 9 3 1 - 3 * + 10 2 / +;
1-1 后缀表达式
#include <iostream>
#include <vector>
#include <stack>
#include <string>
using namespace std;
//比较 lhs 的优先级是否不高于 rhs,rhs表示栈顶的符号
bool priority(const char &lhs, const char &rhs) {
if (rhs == '(') { //左括号在栈外优先级最高
return false;
}
if (lhs == '+' || lhs == '-') {
return true;
}
if ((lhs == '*' || lhs == '/') && (rhs == '*' || rhs == '/')) {
return true;
}
return false;
}
//将中缀表达式转换成后缀式(逆波兰表达式)
string exchange(const string &str) {
vector<char> vec;
string res;
stack<char> st;//操作符堆栈
for (int i = 0; i < str.size(); ++i) {
if (isdigit(str[i])) { //如果是数字,直接输出到后缀表达式中
vec.push_back(str[i]);
} else { //如果是符号,需要与栈顶的元素进行比较
if (st.empty() || str[i] == '(') { //小括号在栈外优先级最高,直接压栈
st.push(str[i]);
} else {
if (str[i] == ')') { //遇到右括号,则弹栈,直到遇到左括号,两者相互抵消
while (!st.empty() && st.top() != '(') {
vec.push_back(st.top());
st.pop();
}
st.pop();
} else { //遇到的是其他操作符
if (priority(str[i], st.top())) { //优先级比栈顶元素低
while (!st.empty()) {
vec.push_back(st.top());
st.pop();
}
st.push(str[i]);
} else { //优先级比栈顶元素高,压栈
st.push(str[i]);
}
}
}
}
}
while (!st.empty()) { //如果堆栈不为空,则将其中的元素全部弹出
vec.push_back(st.top());
st.pop();
}
for (auto v : vec) {
res += v;
}
return res;
}
//定义四则运算
int operate(int first, int second, char op) {
int res = 0;
switch (op) {
case '+':
res = first + second;
break;
case '-':
res = first - second;
break;
case '*':
res = first * second;
break;
case '/':
res = first / second;
break;
default:
break;
}
return res;
}
int calculate(string input) {
stack<int> st;//操作数堆栈
for (auto &s : input) {
if (isdigit(s)) { //如果是数字就压栈
st.push(s - '0');
} else { //遇到字符就弹出两个操作数进行运算
int a = st.top();
st.pop();
int b = st.top();
st.pop();
st.push(operate(b, a, s));
}
}
return st.empty() ? 0 : st.top();//最后的结果为栈顶元素
}
int main() {
string str;
cin >> str;//"6+7*(3+4)-4/2";
cout << exchange(str) << endl;//6734+*+42/-
cout << calculate(exchange(str)) << endl;//53
return 0;
}
1-2 代码运行结果: ![](https://i-blog.csdnimg.cn/blog_migrate/001eceaacd9303910de7f428ee743450.png)
3 . 栈在递归中的应用
递归策略只需少量的代码就可以描述出解题过程所需要的多次重复计算,大大减少了程序的代码量。但在通常情况下,他的效率并不是太高。
- 使用栈可以模拟递归的过程,以此来消除递归;
- 对于单项递归和尾递归而言,可以用迭代的方式来消除递归。
二 、 队列的应用
1 . 队列在层次遍历中的应用
- 树的层次遍历
- 图的广度优先搜索
2 . 队列在计算机系统中的应用
- 缓冲区
- CPU资源的分配(First Come Frist Service)