目录
4. 中缀(Infix),前缀(Prefix),后缀(Postfix)
Stack ADT——Last - In - First - Out (LIFO)
定义:插入和删除操作只能从<栈顶>进行。
栈即为一段封闭的管子:
操作:
(1) Push(x);
(2) Pop();
(3) Top();
(4) IsEmpty();
以上4种操作的时间复杂度都是O(1),即执行的完成时间为常数。
1. 栈的实现
1.1 用数组(Array)实现栈
int A[10];
top = -1; // empty stack
Push(x)
{
top=top+1;
A[top]=x;
}
Pop()
{
top=top-1; //不用考虑删除,cause:1) 不属于栈内;2) 在执行Push(x)时自会覆盖原值
}
但数组长度有限,Push时溢出(overflow):
创建一个两倍的数据,并复制原有数据——时间复杂度O(n),即和栈上的元素个数n成正比。Push:1) 不溢出——O(1)
2) 溢出——O(n)
Top()
{
return A[top];
}
IsEmpty()
{
if ( top==-1)
return true;
else
return false;
}
1.2 用链表(linked list)实现栈
插入/删除:1) 在链表尾部——时间复杂度为O(n),即与链表长度成正比;
2) 在链表开头——时间复杂度为O(1),即常数;
从时间复杂度上考虑,选择在链表开头进行 插入/删除 操作。
代码实现如下:
#include <iostream>
using namespace std;
struct Node
{
int data;
Node* next;
};
Node* top = NULL;
void Push(int x)
{
Node* newnode = new Node;
newnode->data = x;
newnode->next = NULL;
if (top == NULL)
top = newnode;
else
{
newnode->next = top;
top = newnode;
}
}
void Pop()
{
Node* temp = top;
top = top->next;
delete temp;
}
void Print()
{
cout << "Stack : ";
Node* temp = top;
while (temp != NULL)
{
cout << temp->data << '\t';
temp = temp->next;
}
cout << endl;
}
int main()
{
Push(2); Print();
Push(5); Print();
Push(6); Print();
Pop(); Print();
}
2. 栈的应用:检查括号匹配性
伪代码如下:
for ( i <- 0 to n-1 )
{
if ( exp[i] is '(' or '[' or '{' )
push(exp[i]);
else if ( exp[i] is '(' or '[' or '{' )
{
if ( stack is empty ) || ( top doesn't pair with exp[i] )
{
return false;
}
else
pop();
}
}
//检查括号匹配性
#include <iostream>
#include<stdio.h>
#include<string>
#include<stack>
using namespace std;
bool Arepair(char a,char b)
{
if (a == '(' && b == ')') return true;
else if(a == '[' && b == ']') return true;
else if (a == '{' && b == '}') return true;
else return false;
}
bool AreBalance(string exp,int n)
{
stack<char> S;
for (int i = 0; i < n; i++)
{
if (exp[i] == '(' || exp[i] == '[' || exp[i] == '{')
S.push(exp[i]);
else if (exp[i] == ')' || exp[i] == '}' || exp[i] == ']')
{
if (S.empty() || !Arepair(S.top(), exp[i]))
return false;
else
S.pop();
}
}
if (S.empty())
return true;
else
return false; //return S.empty() ? true:false;
}
int main()
{
string exp; //string类
cout << "Input an expression:";
cin >> exp;
int n = exp.length();
if (AreBalance(exp,n))
cout << "Balance\n";
else
cout << "Not Balance\n";
}
3. 中缀(Infix),前缀(Prefix),后缀(Postfix)
Infix | Prefix | Postfix |
2+3 | +2 3 | 2 3+ |
p-q | -q p | q p- |
a+b*c | +a*bc | abc*+ |
human-readable | good for machines |
中缀(Infix): <operand> <operator> <operand>
后缀(Postfix): <operand> <operand> <operator>
后缀:最容易解析,求值时间和内存代价最小的
3.1 解析后缀表达式:
Q:如何解析后缀表达式"2 3*5 4*+9-"?
A:从左至右,<操作数>入栈,直至遇上<操作符>,选择栈顶的两个数进行操作,所得<操作数>入栈,直至遍历,栈顶即为所解。
伪代码如下:
Evaluate_Postfix(exp)
{
stack S;
for ( i = 0 to length(exp)-1 )
{
if ( exp[i] is <operand> )
push(exp[i]);
else if ( exp[i] is <operator>)
{
op2 = pop(); //第一个为操作数2,若为前缀: op1 = pop();
op1 = pop(); //第一个为操作数1,若为前缀: op2 = pop();
result = perform( exp[i], op1 ,op2 )
push(result);
}
}
return top;
}
代码如下:
// 计算后缀表达式的代码
#include <iostream>
#include <stack> //C++自带的stack库
#include <string>
using namespace std;
//2 3 * 5 4 * + 9 -
stack<int> S;
int PerformOperation(char oper);
int EvaluatePost(string exp);
int GetNum(char* num,int* i);
int main()
{
string exp;
cout << "Input an expression:";
getline(cin, exp);
int val = EvaluatePost(exp);
cout << "\nOutput is:"<<val;
}
int EvaluatePost(string exp)
{
for (int i = 0; i < exp.length(); i++)
{
if (exp[i] == ' ' || exp[i] == ',') continue;
else if (exp[i] == '+' || exp[i] == '-' || exp[i] == '*' || exp[i] == '/')
{
int temp = PerformOperation(exp[i]);
S.push(temp);
}
else
{
int num = GetNum(&exp[i],&i);
S.push(num);
}
}
return S.top();
}
int PerformOperation(char oper)
{
int op2 = S.top();
S.pop();
int op1 = S.top();
S.pop(); //
if (oper == '+') return op1 + op2;
else if (oper == '-') return op1 - op2;
else if (oper == '*') return op1 * op2;
else if (oper == '/') return op1 / op2;
}
int GetNum(char* num,int* i)
{
int val = 0;
while (!(*num == ' ' || *num == ',')&&!((*num == '+' || *num == '-' || *num == '*' || *num == '/')))
{
val = val*10 + *num - '0';
num++;
*i= *i+1;
}
*i = *i - 1;
return val;
}
前缀(Prefix): <operator> <operand> <operand>
Q:如何解析前缀表达式"-+*2 3*5 4 9"?
A:从右至左,<操作数>入栈,直至遇上<操作符>,选择栈顶的两个数进行操作,所得<操作数>入栈,直至遍历,栈顶即为所解。
3.2 中缀转化为后缀:
Q1:如何将中缀表达式"A+B*C-D*E"转化为后缀表达式?
A1:观察到,中缀 —》 后缀,<操作数>顺序不变,仅有<操作符>位置改变。可将<操作符>入栈待用,但如何确定<操作符>出栈时机?
从左至右,<操作符>入栈,直至遇上一个更低优先级的<操作符>,所有<操作符>出栈,从而清空栈(此处为所有<操作符>是因为,前面的运算操作均已完成),直至到达表达式结尾。
总结<操作符>出栈时机:a) 遇上一个更低优先级的<操作符>;b) 到达表达式结尾。
伪代码如下:
InfixToPostfix(exp)
{
stack S;
string result; //存储转化结果
for ( i = 0 to length(exp)-1 )
{
if ( exp[i] is <operand> )
result = result + exp[i];
else if ( exp[i] is <operator>)
{
while (!S.empty() && HigherPrec(S.top(), exp[i])); //栈顶是否有更高优先级
{
result = result + S.top();
S.pop();
}
S.push( exp[i] )
}
}
while (!S.empty())
{
result = result + S.top();
S.pop();
}
return result;
}
Q2:如何将带括号的中缀表达式"[(A+B)*C-D]*(E+1*2)"转化为后缀表达式?
A2:括号其实就是一个限制域,只作用于<左括号>到<右括号>,将<左括号>入栈,直至遇上一个<右括号>,这说明这个限制域结束,即为:<操作符>出栈时机b)——到达表达式结尾。
从左至右,<操作符>入栈,直至遇上一个<右括号>,包括<左括号>在内其前的所有<操作符>出栈,直至到达表达式结尾。
伪代码修改如下:
InfixToPostfix(exp)
{
stack S;
string result; //存储转化结果
for ( i = 0 to length(exp)-1 )
{
if ( exp[i] is <operand> )
result = result + exp[i];
else if ( exp[i] is <operator>)
{
while ( !S.empty() && HigherPrec( S.top(), exp[i] )
&& !IsOpeningParentheses(exp[i]) ); //遇到<左括号>则停下,跳出{
result = result + S.top();
S.pop();
}
S.push( exp[i] )
}
else if ( IsOpeningParentheses(exp[i]) ) //<左括号> '(' '[' '{',压栈S.push( exp[i] );
else if ( IsClosingParentheses(exp[i]) ) //<右括号> ')' ']' '}',出栈{
while ( !S.empty() && !IsOpeningParentheses( S.top() )
{result = result + S.top();
S.pop();
}
S.pop(); //弹出对应的为<左括号> '(' '[' '{'
}
while (!S.empty())
{
result = result + S.top();
S.pop();
}
return result;
}
代码实现如下:
//实现中缀转后缀的代码
#include<iostream>
#include<stack>
#include<string>
using namespace std;
string InfixToPostfix(string exp);
bool IsOperand(char x);
bool IsOperator(char x);
bool IsOpening(char x);
bool IsClosing(char x);
bool IsHigher(char op1, char op2);
int GetOperatorWeight(char op);
//((A+B)*C-D)*E
//[(A+B)*C-D]*E
int main()
{
string exp;
cout << "Enter Infix Expression:";
getline(cin, exp);
string postfix = InfixToPostfix(exp);
cout << "Postfix Expression: " << postfix;
}
string InfixToPostfix(string exp)
{
stack<char> S;
string postfix = ""; //置空
for (int i = 0; i <= exp.length() - 1; i++) //string类用 .length()
{
if (exp[i] == ' ' || exp[i] == ',') continue; // 有‘’或者‘,’
else if (IsOperand(exp[i]))
postfix = postfix + exp[i];
else if (IsOperator(exp[i])) //+ - * /
{
while (!S.empty() && IsHigher(S.top(), exp[i])) //栈顶优先级高则出栈
{
postfix = postfix + S.top();
S.pop();
}
S.push(exp[i]);
}
else if (IsOpening(exp[i])) //左括号
S.push(exp[i]);
else if (IsClosing(exp[i])) //右括号
{
while (!S.empty() && !IsOpening(S.top()))
{
postfix = postfix + S.top();
S.pop();
}
S.pop();
}
}
while (!S.empty())
{
postfix = postfix + S.top();
S.pop();
}
return postfix;
}
bool IsOperand(char x)
{
if (x >= '0' && x <= '9') return true;
if (x >= 'a' && x <= 'z') return true;
if (x >= 'A' && x <= 'Z') return true;
return false; //return false; 不能忘
}
bool IsOperator(char x)
{
if (x == '+' || x == '-' || x == '*' || x == '/') return true;
return false; //return false; 不能忘
}
bool IsOpening(char x)
{
if (x == '(' || x == '[' || x == '{') return true;
return false; //return false; 不能忘
}
bool IsClosing(char x)
{
if (x == ')' || x == ']' || x == '}') return true;
return false; //return false; 不能忘
}
bool IsHigher(char op1, char op2) //检测op2是否比栈顶低,低/相同则pop
{
int op1Weight = GetOperatorWeight(op1);
int op2Weight = GetOperatorWeight(op2);
// If operators have equal precedence, return true if they are left associative.
// return false, if right associative.
// if operator is left-associative, left one should be given priority.
return op1Weight >= op2Weight ? true : false;
}
// Function to get weight of an operator. An operator with higher weight will have higher precedence.
int GetOperatorWeight(char op)
{
int weight = -1;
switch (op)
{
case '+':
case '-':
weight = 1;
case '*':
case '/':
weight = 2;
case ')':
case ']':
case '}':
weight = 3;
}
return weight;
}