您的位置:首页 > 理论基础 > 数据结构算法

数据结构之栈(三)

2016-02-19 10:31 453 查看
前面两篇文章介绍了Stack的结构、基本操作和两种实现方式,本文将探讨Stack的应用。Stack的最常见的应用是缓存和处理优先级,Queue也可以用于缓存和处理优先级,但两者的优先级处理方式不同。本文将以“算术表达式”的处理为例,展示Stack的魅力。

一、Arithmetic Expression(点击打开链接

数学中的算术表达式一般是这样的:

(5 + 9)× 2 + 6 × 5


这种表达式的特点是:运算符(opreator)位于算子(operand)中间。因此,它也被称为“infix expresion”。对于人来说,只要有运算符优先级的概念,就很容易理解“infix expression”;而对于计算机来说,它是一个字符一个字符来处理这个“表达式字符串”的,它没有优先级的概念(或者说给它建立优先级的概念,程序会更复杂),不太容易能处理这种表达式。

波兰逻辑学家Jan Lukasiewicz引入了“prefix expresiion”,克服了“infix expression”需要考虑“运算符优先级”和“parenthese”的缺点。“prefix expression”如下:

+ × + 5 9 2 × 6 5


这种“prefix expression”在没有“parenthese”的情况下,完全没有歧义。但是,它对人来说比较难以理解。因此,有人通过添加“parenthese”的方法,让它更容易理解。(事实上,三种算术表达式都可以通过添加“parenthese”来辅助执行顺序的理解,点击打开链接)。

+(×(+(5,9),2),×(6,5))


注:添加圆括号用于辅助理解算术执行的顺序。转换成C++函数表示为:

operator+(operator*(operator+(5,9),2),operator*(6,5)) ;


除了“prefix expresiion”,还有一种“postfix expresiion”,它的运算符在算子后面。如下:

5 9 + 2 × 6 5 × +


二、“Ealuation Postfix Expresiions”(点击打开链接

借助Stack计算机程序可以很容易处理“postfix
expresiion”。程序从左到右按如下规则逐个处理各个“component”(成份):

1)如果表达式的下一个成份是算子,则进栈;

2)如果表达式的下一个成份是算符,则它需要的算子从栈中取出(支持一元算符和二元算符),运算完毕,再将结果入栈。



上图演示了后缀表达式的处理过程。

void RPNCalculator(Stack & stack)
{
char c;
while (cin >> c, !cin.eof())    // eof -- end of file
{
if (std::isdigit(c))
stack.Push(*new Int(c - '0'));
else if (c == '+')
{
Int & arg2 = dynamic_cast<Int&>(stack.Pop());
Int & arg1 = dynamic_cast<Int&>(stack.Pop());
stack.Push(*new Int(arg1 + arg2));
delete &arg1;
delete &arg2;
}
else if (c == '*')
{
Int & arg2 = dynamic_cast<Int&>(stack.Pop());
Int & arg1 = dynamic_cast<Int&>(stack.Pop());
stack.Push(*new Int(arg1 * arg2));
delete &arg1;
delete &arg2;
}
else if (c == '=')
{
Int & arg = dynamic_cast<Int&>(stack.Pop());
cout << arg << endl;
delete &arg;
}
}
}


上面的函数可以计算后缀表达式(仅限10以内正整数),测试代码如下:

int main()
{
StackAsLinkedList stack;
RPNCalculator(stack);

return 0;
}


三、后缀表达式计算扩展

下面展示一个扩展的后缀表达式计算函数,它支持double型数据和负数,同时增加乘方运算和取反运算。

void RPNCalculator(Stack & stack)
{
string s;
do
{
getline(cin, s);    // 使用类似QSttring与QStringList的split()函数,可以录入一行表达式,各元素(operand或Operator)用空格分离
if (s.length() == 0)
continue;

if (s.length() == 1)
{
char c = s[0];
if (std::isdigit(c))
stack.Push(*new Double(c - '0'));
else if (c == '+')
{
Double & arg2 = dynamic_cast<Double&>(stack.Pop());
Double & arg1 = dynamic_cast<Double&>(stack.Pop());
stack.Push(*new Double(arg1 + arg2));
delete &arg1;
delete &arg2;
}
else if (c == '-')
{
Double & arg2 = dynamic_cast<Double&>(stack.Pop());
Double & arg1 = dynamic_cast<Double&>(stack.Pop());
stack.Push(*new Double(arg1 - arg2));
delete &arg1;
delete &arg2;
}
else if (c == '*')
{
Double & arg2 = dynamic_cast<Double&>(stack.Pop());
Double & arg1 = dynamic_cast<Double&>(stack.Pop());
stack.Push(*new Double(arg1 * arg2));
delete &arg1;
delete &arg2;
}
else if (c == '/')
{
Double & arg2 = dynamic_cast<Double&>(stack.Pop());
Double & arg1 = dynamic_cast<Double&>(stack.Pop());
stack.Push(*new Double(arg1 / arg2));
delete &arg1;
delete &arg2;
}
else if (c == '^')
{
Double & arg2 = dynamic_cast<Double&>(stack.Pop());
Double & arg1 = dynamic_cast<Double&>(stack.Pop());
stack.Push(*new Double(std::pow(arg1, arg2)));
delete &arg1;
delete &arg2;
}
else if (c == '!')
{
Double & arg = dynamic_cast<Double&>(stack.Pop());
stack.Push(*new Double(-arg));
delete &arg;
}
else if (c == '=')
{
Double & arg = dynamic_cast<Double&>(stack.Pop());
cout << arg << endl;
delete &arg;
}
}
else
{
double operand;    //double operand = atof(str); //for C Language
stringstream ss;
ss << s;
ss >> operand;
stack.Push(*new Double(operand));
}

} while (!cin.eof());
}


注:C++11的 <cstdlib>中也提供了“atof函数”。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: