栈与队列-栈
1. 栈
栈(Stack)是限定仅在表尾进行插入和删除操作的线性表
把允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom),不含任何数据元素的栈称为空栈。栈又称为后进先出(Last in First Out,LIFO)的线性表,简称LIFO结构。
栈的插入操作,叫做进栈,压栈或入栈。
栈的删除操作,叫做出栈,弹栈*。
2. 顺序栈
栈是线性表的特例,栈的顺序存储结构,也称为顺序栈。
2.1 顺序栈的结构定义
#define MAXSIZE 20
typedef int ElemType; // 数据类型
struct SqStack
{
ElemType data[MAXSIZE]; // 存放数据
int top; // 栈顶指针 -1表示空栈 最大值为MAXSIZE-1
};
2.2 入栈操作
bool Push(SqStack* S,ElemType e)
{
if(S==nullptr||S->top==MAXSIZE-1) // 栈不存在或栈满
{
return false;
}
// 入栈
// 1.栈顶指针后移
S->top++;
// 2.元素入栈
S->data[S->top]=e;
return true;
}
2.3 出栈操作
bool Pop(SqStack* S,ElemType *e)
{
if(S==nullptr||S->top==-1) // 栈不存在或栈为空
{
return false;
}
// 保存栈顶元素
*e = S->data[S->top];
// 删除元素
S->top--;
return true;
}
3.链栈
栈有顺序存储结构,也有链式存储结构,简称为链栈。
3.1 链栈结构
struct StackNode
{
ElemType data; // 数据域
StackNode* next; // 指针域
};
typedef StackNode* StackPtr; // 栈顶指针
struck LinkStack // 链栈
{
StackPtr top; // 栈顶指针
int count; // 栈元素个数
};
3.2 进栈操作
bool Push(LinkStack* S,ElemType e)
{
if(S==nullptr) // 栈不存在
{
return false;
}
// 插入元素
// 1. 准备结点
StackNode* new_node = new StackNode;
new_node->data = e;
// 2. 插入结点
new_node->next = S->top;
S->top = new_node;
// 维护大小
S->count++;
return true;
}
3.3 出栈操作
bool Pop(LinkStack*S, ElemType *e)
{
if(S==nullptr||S->top==nullptr) // 栈不存在或栈为空
{
return false;
}
// 弹出栈顶元素
// 1. 存储栈顶元素
StackNode* top_node = S->top;
*e = top_node->data;
// 2. 弹出
S->top=top_node->next;
delete top_node;
return true;
// 维护长度
S->count--;
return true;
}
4. 栈的应用
4.1 递归:斐波那契(Fibonacci)数列的实现
斐波那契数列的表达式为:
F
(
n
)
=
{
0
n
=
0
1
n
=
1
F
(
n
−
1
)
+
F
(
n
−
2
)
n
>
1
F\left(n\right)=\left\{ \begin{matrix} 0&n=0\\ 1&n=1\\ F\left(n-1\right)+F\left(n-2\right)&n>1 \end{matrix} \right.
F(n)=⎩
⎨
⎧01F(n−1)+F(n−2)n=0n=1n>1
利用递归的思想实现斐波那契数列
// 计算Fibonacci数列的第n项
int Fibonacci(int n)
{
if (n < 2)
return n == 0 ? 0 : 1;
return Fibonacci(n - 1) + Fibonacci(n - 2);
}
int main(void)
{
int a[40];
for(int i = 0;i<40;i++)
{
a[i] = Fibonacci(i);
cout<<a[i]<<" ";
}
cout<<endl;
return 0;
}
4.1.1 递归的定义
把一个直接调用自己或通过一系列操作间接调用自己的函数,称作递归函数。每个递归必须至少有一个条件,满足时递归不再进行,即不再调用自身而是返回值推出。
递归和栈的关系:在前行阶段,随着对每一层的递归,函数的局部变量,参数值和返回地址都被压入栈中。在退回阶段,位于栈顶的局部变量、参数值和返回地址被弹出,用于返回调用层中执行代码其余的部分。
4.2 四则运算表达式求值
4.2.1 后缀表示法
一种不需要括号的后缀表示法,也称为逆波兰(RPN)表示。例如对于公式
9
+
(
3
−
1
)
×
3
+
8
÷
2
9+(3-1)\times3+8\div2
9+(3−1)×3+8÷2的后缀表达式为:
9
3
1
−
3
×
+
8
2
÷
+
9\:3\:1-3\times\:+\:8\:2\div +
931−3×+82÷+叫做后缀表达式的原因在于所有的符号都是要在运算数字后面出现
我们平时所用的标准四则运算表达式即
9
+
(
3
−
1
)
×
3
+
8
÷
2
9+(3-1)\times3+8\div2
9+(3−1)×3+8÷2叫做中缀表达式,因为所有的运算符号都在两个数字的中间。如何实现中缀表达式转后缀表达式呢?
规则:从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级,是右括号或优先级不高于栈顶符号(乘除优先于加减)则栈顶元素出栈并输出,并将当前符号进栈,直到输出整个表达式。
- 初始化空栈,用来对符号进出栈使用。
- 第1个字符是9,输出9,后面是符号 + + +,进栈。栈中元素: + + +
- 第3个字符是 ( \left(\right. (,进栈。栈中元素: ( , + \left(\right.,+ (,+
- 第4个字符是3,输出3,后面的符号是 − - −,进栈。栈中元素: − , ( , + -,\left(\right.,+ −,(,+
- 第6个字符是1,输出1,后面的符号是右括号。此时需要匹配左括号,栈中元素依次输出,直到左括号出栈。输出-。栈中元素: + + +
- 第8个字符是 × \times ×,进栈。栈中元素: × , + \times,+ ×,+。
- 第9个字符是3,输出3。后面的符号是 + + +,不高于栈顶元素 × \times ×,栈顶元素出栈并输出 × \times ×, + + +不高于栈顶元素 + + +,栈顶元素出栈,输出+。字符入栈。栈中元素: + + +。
- 第11个字符是8,输出8。后面的符号是 ÷ \div ÷,优先级高于栈顶元素,输出 ÷ \div ÷。
- 第12个字符是2,输出2。栈不为空,输出栈中元素
+
+
+。
此时输出的表达式为: 9 3 1 − 3 × + 8 2 ÷ + 9\:3\:1-3\times\:+\:8\:2\div + 931−3×+82÷+
具体实现为:
#include <iostream>
#include <stack>
using namespace std;
// 判断是否为括号
bool isParenthesis(const char c)
{
if (c == '(' || c == ')')
return true;
return false;
}
// 比较两个符号的优先级
int getPri(char c)
{
switch (c) {
case '+':
case '-':
return 0; // 如果是加减,返回0
break;
case '*':
case '/':
return 1; // 如果是乘除,返回1
break;
case '(':
case ')':
return -1; // 这里将括号设为最低优先级,因此括号不会被弹出,除非遇到右括号
break;
default:
break;
}
}
// 中缀表达式转后缀表达式
string Infix2Postfix(const string& input)
{
std::stack<char> Stack; // 定义栈
string output;
for (int i = 0; i < input.size(); i++)
{
if (input[i] >= '0' && input[i] <= '9') // 数字,直接输出
{
output.push_back(input[i]);
output.push_back(' ');
continue;
}
// 判断是否为括号
if (isParenthesis(input[i]))
{
// 是左括号入栈
if (input[i]=='(')
{
Stack.push(input[i]);
}
else // 是右括号,弹栈并输出,直到弹栈出左括号
{
char out = NULL;
while (Stack.top()!='(')
{
output.push_back(Stack.top());
output.push_back(' ');
Stack.pop();
}
// 弹出左括号
Stack.pop();
}
}
// 不是数字也不是括号,是运算符
else
{
// 比较当前符号与栈顶元素的优先级
// 高于栈顶元素,入栈
if (Stack.empty()||getPri(input[i]) > getPri(Stack.top()))
{
Stack.push(input[i]);
}
else // 不高于栈顶元素,将栈顶元素依次出栈,并输出,当前元素入栈
{
while (!Stack.empty() && getPri(input[i]) <= getPri(Stack.top()))
{
output.push_back(Stack.top()); // 栈顶元素输出
output.push_back(' ');
Stack.pop(); // 出栈
}
// 元素入栈
Stack.push(input[i]);
}
}
}
while (!Stack.empty()) // 将栈中剩余元素输出
{
output.push_back(Stack.top());
output.push_back(' ');
Stack.pop();
}
return output;
}
int main(void)
{
string input;
cout << "输入运算表达式:";
cin >> input;
string out=Infix2Postfix(input);
cout << out << endl;
return 0;
}