数据结构之栈(三)
2016-02-19 10:31
453 查看
前面两篇文章介绍了Stack的结构、基本操作和两种实现方式,本文将探讨Stack的应用。Stack的最常见的应用是缓存和处理优先级,Queue也可以用于缓存和处理优先级,但两者的优先级处理方式不同。本文将以“算术表达式”的处理为例,展示Stack的魅力。
一、Arithmetic Expression(点击打开链接)
数学中的算术表达式一般是这样的:
这种表达式的特点是:运算符(opreator)位于算子(operand)中间。因此,它也被称为“infix expresion”。对于人来说,只要有运算符优先级的概念,就很容易理解“infix expression”;而对于计算机来说,它是一个字符一个字符来处理这个“表达式字符串”的,它没有优先级的概念(或者说给它建立优先级的概念,程序会更复杂),不太容易能处理这种表达式。
波兰逻辑学家Jan Lukasiewicz引入了“prefix expresiion”,克服了“infix expression”需要考虑“运算符优先级”和“parenthese”的缺点。“prefix expression”如下:
这种“prefix expression”在没有“parenthese”的情况下,完全没有歧义。但是,它对人来说比较难以理解。因此,有人通过添加“parenthese”的方法,让它更容易理解。(事实上,三种算术表达式都可以通过添加“parenthese”来辅助执行顺序的理解,点击打开链接)。
注:添加圆括号用于辅助理解算术执行的顺序。转换成C++函数表示为:
除了“prefix expresiion”,还有一种“postfix expresiion”,它的运算符在算子后面。如下:
二、“Ealuation Postfix Expresiions”(点击打开链接)
借助Stack计算机程序可以很容易处理“postfix
expresiion”。程序从左到右按如下规则逐个处理各个“component”(成份):
1)如果表达式的下一个成份是算子,则进栈;
2)如果表达式的下一个成份是算符,则它需要的算子从栈中取出(支持一元算符和二元算符),运算完毕,再将结果入栈。
![](http://img.blog.csdn.net/20160219101930602?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
上图演示了后缀表达式的处理过程。
上面的函数可以计算后缀表达式(仅限10以内正整数),测试代码如下:
三、后缀表达式计算扩展
下面展示一个扩展的后缀表达式计算函数,它支持double型数据和负数,同时增加乘方运算和取反运算。
注:C++11的 <cstdlib>中也提供了“atof函数”。
一、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函数”。
相关文章推荐
- C#中使用Redis不同数据结构的内存占有量的疑问和对比测试
- Java数据结构----优先队列
- 数据结构
- 学习笔记------数据结构(C语言版)数组之行逻辑链接的顺序表
- 数据结构——二叉树
- 学习笔记------数据结构(C语言版)数组之三元组顺序表
- 常用排序的实现方法(数据结构)
- Java数据结构----链表
- Java用数据结构解决实现问题之数学问题
- Java数据结构----队列实现及源码分析
- 数据结构和算法 笔记
- 数据结构 约瑟夫环问题C++
- nginx源码初读(1)--让烦恼从数据结构开始(ngx_cdecl/ngx_int/ngx_log)
- 数据结构与算法
- 图的邻接矩阵c语言表示(无向网)---《数据结构》算法7.2
- 数据结构 魔王语言C++
- 数据结构——树
- 数据结构之栈(二)
- 数据结构之栈(一)
- 数据结构在程序中的实现及表现形式