栈之经典应用
来源:互联网 发布:java 字符串gbk编码 编辑:程序博客网 时间:2024/05/19 10:35
算术表达式的求值
栈的应用
由于栈结构具有的后进先出的固有特性,致使栈成为程序设计中常用的工具。以下是几个栈应用的例子。
一、算术表达式的中缀表示
把运算符放在参与运算的两个操作数中间的算术表达式称为中缀表达式。例如:2+3*4 – 6/9
算术表达式中包含了算术运算符和算术量(常量、变量、函数),而运算符之间又存在着优先级,不能简单地进行从左到右运算,编译程序在求值时,不能简单从左到右运算,必须先算运算级别高的,再算运算级别低的,同一级运算才从左到右。在计算机中进行中缀表达式求值较麻烦。而后缀表达式求值较方便(无须考虑运算符的优先级及圆括号)。
二、算术表达式的后缀表示
把运算符放在参与运算的两个操作数后面的算术表达式称为后缀表达式。
例如,对于下列各中缀表达式:
(1)3/5+8
(2)18-9*(4+3)
对应的后缀表达式为:
(1)3 5 / 8 +
(2)18 9 4 3 + * -
转换规则:把每个运算符都移到它的两个操作数的后面,然后删除掉所有的括号即可.
例如,将中缀表达式a+b*c-d(e*f)转换为后缀表达式.
三、后缀表达式的求值
将中缀表达式转换成等价的后缀表达式后,求值时,不需要再考虑运算符的优先级,只需从左到右扫描一遍后缀表达式即可。具体求值步骤为:从左到右扫描后缀表达式,遇到运算符就把表达式中该运算符前面两个操作数取出并运算,然后把结果带回后缀表达式;继续扫描直到后缀表达式最后一个表达式。
例如,后缀表达式(abc*+def*/-)的求值
四、后缀表达式的求值的算法
设置一个栈,开始时,栈为空,然后从左到右扫描后缀表达式,若遇操作数,则进栈;若遇运算符,则从栈中退出两个元素,先退出的放到运算符的右边,后退出的放到运算符左边,运算后的结果再进栈,直到后缀表达式扫描完毕。此时,栈中仅有一个元素,即为运算的结果。
例,求后缀表达式:1 2 + 8 2 - 7 4 - / *的值,
栈的变化情如下:
步骤栈中元素说明111进栈2122进栈3 遇+号退栈2和1431+2=3的结果3进栈5388进栈63822进栈73遇-号退栈2和88368-2=6的结果6进栈93677进栈1036744进栈1136遇-号退栈4和712367-4=3的结果3进栈133遇/号退栈3和614326/3=2的结果2进栈15 遇*号退栈2和31663*2=6进栈176扫描完毕,运算结束
从上可知,最后求得的后缀表达式之值为6,与用中缀表达式求得的结果一致,但后缀式求值要简单得多。
五、中缀表达式变成等价的后缀表达式的算法
将中缀表达式变成等价的后缀表达式,表达式中操作数次序不变,运算符次序发生变化,同时去掉了圆括号。转换规则是:设立一个栈,存放运算符,首先栈为空,编译程序从左到右扫描中缀表达式,若遇到操作数,直接输出,并输出一个空格作为两个操作数的分隔符;若遇到运算符,则必须与栈顶比较,运算符级别比栈顶级别高则进栈,否则退出栈顶元素并输出,然后输出一个空格作分隔符;若遇到左括号,进栈;若遇到右括号,则一直退栈输出,直到退到左括号止。当栈变成空时,输出的结果即为后缀表达式。将中缀表达式(1+2)*((8-2)/(7-4))变成等价的后缀表达式。
现在用栈来实现该运算,栈的变化及输出结果如下:
1、 数制转换
将一个非负的十进制整数N转换为另一个等价的基为B的B进制数的问题,很容易通过"除B取余法"来解决。
【例】将十进制数13转化为二进制数。
解答:按除2取余法,得到的余数依次是1、0、1、1,则十进制数转化为二进制数为1101。
分析:由于最先得到的余数是转化结果的最低位,最后得到的余数是转化结果的最高位,因此很容易用栈来解决。
转换算法如下:
typedef int DataType;//应将顺序栈的DataType定义改为整型
void MultiBaseOutput (int N,int B)
{//假设N是非负的十进制整数,输出等值的B进制数
int i;
SeqStack S;
InitStack(&S);
while(N){ //从右向左产生B进制的各位数字,并将其进栈
push(&S,N%B); //将bi进栈0<=i<=j
N=N/B;
}
while(!StackEmpty(&S)){ //栈非空时退栈输出
i=Pop(&S);
printf("%d",i);
}
}
以上部分原文地址:http://sjjp.tjuci.edu.cn/sjjg/DataStructure/DS/web/zhanhuoduilie/zhanhuoduilie3.3.1.htm
栈是计算机术语中比较重要的概念,实质上栈就是一段内存区域,但是栈满足一定的特性,那就是只有一个口,具有先入后出的特性,这种特性在计算机中有很广泛的运用。其实在程序员无时无刻不在运用栈,函数的调用是我们间接使用栈的最好例子,因此可以说栈的一个最重要的运用就是函数的调用。但是栈的运用还不止这些,还有很多,其中几个典型的运行如下:判断平衡符号,实现表达式的求值(也就是中缀表达式转后缀表达式的问题以及后缀表达式求值问题),在路劲探索中实现路劲的保存也可以说是栈的经典运用之一。具体的问题具体分析,只要满足先入后出特性的问题都能找到现成的数据结构---栈。
- #ifndef __MYSTACK_H_H_
- #define __MYSTACK_H_H_
- #include "myvector.h"
- namespace myspace
- {
- template<typename Object>
- class Stack
- {
- public:
- Stack(){}
- void push(const Object&x)
- {
- objects.push_back(x);
- }
- const Object &pop()
- {
- int len;
- if(!isempty())
- {
- objects.pop_back();
- len = objects.size();
- return objects[len];
- }
- }
- bool isempty()const
- {
- return (objects.size()== 0);
- }
- int size()
- {
- return objects.size();
- }
- private:
- Vector<Object> objects;
- };
- }
- #endif
- bool isbalance(conststring&str)
- {
- string::size_typelen= str.size();
- Stack<char> stack;
- for(string::size_type i= 0; i < len ; ++ i)
- {
- /*first selection*/
- if(str[i]=='['|| str[i]=='{'||
- str[i]=='('|| str[i]=='<')
- {
- stack.push(str[i]);
- }
- /*如果是对称的符号,则从栈中弹出*/
- if(str[i]==']'|| str[i]=='}'||
- str[i]==')'|| str[i]=='>')
- {
- /*如果栈中没有对象,则说明不平衡*/
- if(stack.isempty())
- {
- cout <<"the string is Unblanced"<< endl;
- return false;
- }
- /*采用switch-case语句判断*/
- switch(str[i])
- {
- case ']':
- {
- /*判断是否是匹配的*/
- if('['!= stack.pop())
- {
- cout << "Can not blanced with ]"<< endl;
- return false;
- }
- break;
- }
- case ')':
- {
- if('('!= stack.pop())
- {
- cout << "Can not blanced with )"<< endl;
- return false;
- }
- break;
- }
- case '}':
- {
- if('{'!= stack.pop())
- {
- cout << "Can not blanced with }"<< endl;
- return false;
- }
- break;
- }
- case '>':
- {
- if('<'!= stack.pop())
- {
- cout << "Can not blanced with >"<< endl;
- return false;
- }
- break;
- }
- /*一般的非对称字符*/
- default:
- break;
- }
- }
- }
- /************************************************
- *当所有对象遍历完成以后,需要判断栈中是否存在对象
- *栈中为空说明是平衡的,非空则说明是非空的对象
- ************************************************/
- if(stack.isempty())
- {
- cout <<"string is blanced!!"<< endl;
- return true;
- }
- else/*没有正确的匹配,并输出具体不匹配的符号*/
- {
- cout << stack.pop()<<" " <<"Unblance string!!"<< endl;
- return false;
- }
- }
- /*****************************************
- * 实现表达式中缀到表达式后缀的转换
- *****************************************/
- bool midtolast(string&src,string&dst)
- {
- string::size_typelen= src.size();
- /*用来保存栈中弹出的对象*/
- char temp = '\0';
- Stack<char> stack;
- for(string::size_type i= 0; i != len;++ i)
- {
- switch(src[i])
- {
- /*如果是'(',则将左括号压入栈中*/
- case '(':
- {
- stack.push(src[i]);
- break;
- }
- /*如果是')',则将栈中左括号之前的对象弹出*/
- case ')':
- {
- /*如果为空,则说明表达式不准确*/
- if(stack.isempty())
- {
- return false;
- }
- /*非空,弹出对象*/
- temp = stack.pop();
- /*只要不是左括号,则全部弹出*/
- while('('!= temp )
- {
- /*并输出到后缀表达式中*/
- dst += temp;
- /*保证栈为非空*/
- if(stack.isempty())
- break;
- /*弹出新的对象*/
- temp = stack.pop();
- }
- /*如果弹出的是左括号,则不用输出到后缀表达式*/
- break;
- }
- /***************************************
- 乘除法操作实质上是一致的
- 在压入栈中之前,需要弹出非左括号的同优先级对象
- 遇到左括号或者低优先级的对象就停止,直接入栈
- ****************************************/
- case '*':
- case '/':
- {
- /*判断非空的栈,为空则直接入栈即可*/
- if(!stack.isempty())
- {
- temp = stack.pop();
- while(temp!='+'&& temp !='-'&& temp!='(')
- {
- dst += temp;
- if(stack.isempty())
- {
- /*保证temp不影响后面的压栈操作*/
- temp = '\0';
- break;
- }
- temp = stack.pop();
- }
- }
- /*如果当前的temp是+-(,则返回到栈中*/
- if(temp=='+'|| temp =='-'|| temp=='(')
- {
- stack.push(temp);
- }
- /*将当前操作符压栈*/
- stack.push(src[i]);
- break;
- }
- case '-':
- case '+':
- {
- /*判断非空*/
- if(!stack.isempty())
- {
- /*加减操作的优先级最低,因此需要弹出所有非左括号的操作符*/
- temp = stack.pop();
- while(temp!='(')
- {
- /*将操作符输出到后缀表达式中*/
- dst += temp;
- if(stack.isempty())
- {
- temp = '\0';
- break;
- }
- temp = stack.pop();
- }
- }
- /*如果当前弹出的对象是’(‘,则重新压入栈中*/
- if(temp=='(')
- {
- stack.push(temp);
- }
- /*将操作符本身压入栈中*/
- stack.push(src[i]);
- break;
- }
- default:
- {
- /*针对字符而言直接输出到后缀表达式*/
- dst += src[i];
- break;
- }
- }
- }
- /*将栈中非空的操作符输出到后缀表达式中*/
- while(!stack.isempty())
- {
- temp = stack.pop();
- dst += temp;
- }
- return true;
- }
后缀表达式求值的问题,实现了后缀表达式的转换,实现表达式的求值问题也就比较简单了,实现的基本思路如下:
- /*后缀表达式求值问题*/
- int retVal(conststring&src)
- {
- Stack<int> stack;
- int temp = 0, s= 0;
- int len= src.size();
- for(string::size_type i= 0; i != len;++ i)
- {
- /*本程序只能处理整形的加减乘除操作*/
- switch(src[i])
- {
- /*遇到数值直接压入栈中*/
- case '0':case'1':case'2':case'3':case'4':
- case '5':case'6':case'7':case'8':case'9':
- {
- stack.push(myatoi(src[i]));
- break;
- }
- /*********************************************
- * 遇到操作符弹出两个数值进行操作
- * 需要注意减法和除法的压栈顺序
- * 操作完成以后将得到的结果压入到栈中
- * 最后栈中的值就是计算的结果
- **********************************************/
- case '+':
- {
- temp = stack.pop()+ stack.pop();
- stack.push(temp);
- temp = 0;
- break;
- }
- case '-':
- {
- s = stack.pop();
- temp = stack.pop()- s;
- stack.push(temp);
- s = 0;
- temp = 0;
- break;
- }
- case '*':
- {
- temp = stack.pop()* stack.pop();
- stack.push(temp);
- temp = 0;
- break;
- }
- case '/':
- {
- s = stack.pop();
- if(s!= 0)
- {
- temp = stack.pop()/ s;
- }
- stack.push(temp);
- s = 0;
- temp = 0;
- break;
- }
- }
- }
- /*获得最后的值*/
- return stack.pop();
- }
为了分析上面的代码准确性,写了如下的测试代码:
- int main()
- {
- string s1;
- cout <<"Input a string to test the balance :"<< endl;
- cin >> s1;
- isbalance(s1);
- #if 10
- string src;
- string dst;
- cout <<"Input a expression: "<< endl;
- cin >> src;
- midtolast(src,dst);
- cout <<"After the convertion: "<< endl;
- cout << dst<< endl;
- cout <<"The result of expression: "<< endl;
- cout << retVal(dst)<< endl;
- #endif
- return 0;
- }
测试结果:
- [gong@Gong-Computer data_struct]$ vi testblance.cpp
- [gong@Gong-Computer data_struct]$ g++-g testblance.cpp-o testblance
- [gong@Gong-Computer data_struct]$./testblance
- Input a string to test the balance:
- This(is)[a]{te}<st>.
- string is
- Input a expression:
- 3*3-(5-2+3*6)*(7-3*1)
- After the convertion:
- 33*52-36*+731*-*-
- The result of expression:
- -75
以上部分的原文地址是:http://blog.chinaunix.net/uid-20937170-id-3322943.html
- 栈之经典应用
- 栈的经典应用
- AJAX经典应用之读取响应首部
- 矩阵乘法经典应用之坐标变化
- 矩阵乘法经典应用之置换
- 数组经典应用之—排序系列
- 单调栈的经典应用
- Android 蓝牙模块之应用层之经典蓝牙
- ABAP动态生成经典应用之Dynamic SQL Excute 程序
- ABAP动态生成经典应用之Table数据Upload 程序
- ABAP动态生成经典应用之Dynamic SQL Excute 程序
- ABAP动态生成经典应用之Dynamic SQL Excute 程序
- ABAP动态生成经典应用之Dynamic SQL Excute 程序
- ABAP动态生成经典应用之Dynamic SQL Excute 程序
- Android研究院之应用开发线程池的经典使用
- AJAX经典应用之动态加载列表框
- Android研究院之应用开发线程池的经典使用
- ABAP动态生成经典应用之Table数据Upload 程序
- python抓取百度贴吧高清图片
- 为什么我们喜欢用 sigmoid 这类 S 型非线性变换?
- Cypress固件架构彻底解析及USB枚举
- java正则表达式语法
- 大话设计模式十一:迪米特法则(无熟人难办事)
- 栈之经典应用
- 解决Ubuntu下Flash多显示器不能全屏问题
- Guava学习之Splitter
- 个风格恢复
- OftenTap親密度通訊錄: 聯繫人親密度管理, 群發短信/郵件, 快捷撥號器
- D3D9 Samples(2)--Vertices
- 学习数据结构中一些C语言问题集锦2
- 排序算法--快速排序相关
- Ubuntu下Eclipse显示Failed to run Android SDK manager(以12.04为例)