栈的应用:进制转换和简易计算器的实现
2010-05-06 11:36
489 查看
1.用c和c++下用栈实现数的进制转换
method1: C的实现
method2: 用c++类的实现
发现上述两种方法只是实现了10进制向2进制的转换,不难发现很容易将上述方法应用到10进制向x进制的转换(x<=10),由于栈的类型是唯一的,若要扩展到10+x进制,还需要进行适当的改进。
对c的实现方法,只需将stackEle改为char,预先定义一个char m[] = "0123456789ABCDEF";,就可以解决问题了;对类的实现方法,也是只要做类似的改进。
2. 用栈实现一个小的计算器
主要是要考虑括号和优先级。将中序形式的输入转换为后序形式,也称为逆波兰表示(RPN)。
2.1 中序表达式转为后序表达式的直观方法
A:扫描中序表达式,碰到操作数(operand)后放入结果串中。
B:碰到操作符(operator)时,如果能确认其操作数,将操作数放入结果串中,否则,将操作符压入栈中。到表达式最后,将栈中的剩余操作符出栈。
拿a+b*c举例:首先a放入结果串中,+入栈,b放入结果串后结果成为ab,遇到*,*的优先级比+大,*入栈,这时栈中为+*,最后C放入结果串,成为abc,扫描到结尾了,将栈中操作符+*按LIFO的原则出栈,最终的结果为:abc*+
2.2 利用后序表示计算的方法
A:例如4+3*5,后序表示是4 3 5 * +:计算时采用这样的方法:
将操作数435依次压入栈
读到操作符*时,计算栈顶两操作数3 5在*下的结果,将结果15压入栈中,此时栈中元素为4 15
读取+,计算4+15,将结果19压入栈
扫描接触,19即是结果
B:类的设计
2.3中序表示式转后序
由于计算机器的面向对象是人,输入的时候不能让一个不会计算机的人输入他要求的式子的后序形式,因而必须设计一种算法,能够自动根据中序输入得到其相应的后序形式,这样,再调用上面的类进行计算时就可以得出结果了。
在中序转后序时,主要是要考虑运算符的个数、优先级(precedence)、结合性(associativity)等。
为了简化计算,仅对含二元操作符的表达式进行讨论。那么,对这样的表达式,若对操作数赋权1,操作符赋权-1,则一个正确输入的中序表达式的权值累加和应该为1,且在中间的任何一个累加点,累加和都应该介于0,1两值(因为二元操作符时,操作数的数目总比操作符数目多1)。
A 算法设计中应该要考虑到的问题
运算符优先级问题:如a*b+c,解决这个问题的方式是对运算符赋予栈优先级,将操作数添加到结果串中,运算符入栈,入栈时,如果栈顶元素的运算符优先级要大于当前元素的有限级,则栈顶元操作符要出栈,放入结果串中,例如当遇到+时,+<*,因而结果串为ab*,栈内为+,最后为ab*+。
解决^(指数计算符)等具有右结合性的元素的问题:如a^b^c=(a^(b^c))!=(a^b)^c,方法是给每个运算符再增加一个输入有限级,右结合性运算符的输入优先级大于其栈优先级,其它非右结合性运算符的输入优先级等于其栈优先级,这样,我们只要比较当前待压入栈的运算符的输入优先级和栈顶元素的栈优先级就可以确定一个运算符是否入栈的问题了,比如,如果新来操作符的栈优先级大于或等于栈顶运算符的栈优先级,则新来操作符入栈;否则,栈顶元素出栈。这将运算符的优先级的问题同时考虑进来了。
解决括号的问题:在原来的基础上,将左括号的输入优先级设为最大,栈优先级设为最小,这样,碰到左括号时,左括号总是要继续入栈,左括号后的操作符也总要继续入栈,剩下的规则沿用上面的,直到出现右括号时,才将左括号之前的操作符依次出栈,并丢弃左括号。
优先级设置:
符号 输入优先级 栈优先级 rank值
+ - 1 1 -1
*/% 2 2 -1
^ 4 3 -1
( 5 -1 0
) 0 0 0
B类的设计
参考文献
[1] K&R The C Programming Language (the second edition)
[2] 《c++编程风格》(c++ programming style )作者 Tom Cargill 译者 聂雪军 机械工业出版社2007.1
[3] Data Structures with C++ using STL, 2nd Edition William H. Ford William R. Topp
类的设计程序来自文献[3]
method1: C的实现
//stack.h #ifndef STACK_H #define STACK_H #define STACK_CAPACITY 20//maximum size of stack typedef int stackEle; typedef struct { stackEle myArray[ STACK_CAPACITY ]; int myTop; }stack; //construct(initialize) an empty stack stack *stack_init(void); //return 1 if stack is empty and 0 otherwise int empty(stack *); //retrieve top element of the stack stackEle top(stack* ); //add an element at the top of the stack void push(stack*,stackEle); //remove the top element of the stack void pop(stack* ); #endif //stack.c #include <stdio.h> #include "stack.h" #include <assert.h> #include <stdlib.h> //construct (initialize) an empty stack stack* stack_init(void) { stack* s = (stack*)malloc(sizeof(stack)); assert(s); s->myTop = -1;//stack is empty return s; } //return 1 if stack is empty and 0 otherwise int empty(stack *s) { assert(s); if(0 > s->myTop) return 1; else return 0; } //retrieve top element of the stack stackEle top(stack* s) { assert(s); if(0 > s->myTop) { printf("Error:stack is empty--no value can be reported/n"); exit(1); } return s->myArray[s->myTop]; } //add an element at the top of the stack void push(stack* s,stackEle val) { assert(s); s->myTop++; if(STACK_CAPACITY < s->myTop)//check if overflow { printf("Error:stack is full--can't add new value/n"); exit(1); } s->myArray[s->myTop] = val; } //remove the top element of the stack void pop(stack* s) { assert(s); if(0 > s->myTop)//check if underflow { printf("Error:stack is empty--can't remove a value/n"); exit(1); } printf("%d ",s->myArray[s->myTop--]); } //do base-10 to base-2 transformation by array-based stack int main() { stack *mystack = stack_init(); int a = 255,b,base = 2; do { b = a % base; push(mystack,b); a = a / base; } while(a != 0); while(!empty(mystack)) pop(mystack); return 0; }
method2: 用c++类的实现
#include<assert.h> #include<string.h> #include<iostream> #include <cmath> using namespace std; class StackIndex { private: int top; int size; public: StackIndex (int sz = 0); ~StackIndex(); int push(); int pop(); int GetSize() {return size;} }; StackIndex::StackIndex (int sz) { size = sz; top = 0; } StackIndex::~StackIndex() { } int StackIndex::push() { assert (top < size); return top++; } int StackIndex::pop() { assert (top > 0); return --top; } const int defaultStack = 128; class CharStack { private: StackIndex index; char* data; public: CharStack (int size = defaultStack, const char* init = " "); ~CharStack(); void push (char); char pop(); void print(); StackIndex GetIndex() { return index;} }; CharStack::CharStack (int size, const char* init) : index (size) { data = new char[size]; for (int i = 0; i < strlen(init); ++i) data[index.push()] = init[i]; } CharStack::~CharStack() { delete [] data; } void CharStack::push (char d) { data[index.push() ] = d; } char CharStack::pop() { return data[index.pop() ]; } void CharStack::print() { int i = index.GetSize() - 1; while(i >= 0) { cout << data[i--] << endl; } } class IntStack { private: StackIndex index; int* data; public: IntStack (int size = defaultStack); ~IntStack(); void push (int); int pop(); void print(); }; IntStack::IntStack (int size) : index (size) { data = new int[size]; } IntStack::~IntStack() { delete [] data; } void IntStack::push (int d) { data[index.push() ] = d; } int IntStack::pop() { return data[index.pop()]; } void IntStack::print() { int i = index.GetSize() - 1; while(i >= 0) { cout << data[i--] << endl; } } //do base-10 to base-2 transformation by array-based stack int main() { int a = 255,b,base = 2; b = log(a) / log(base) + 1;//compute the length of a's binary express IntStack transform(b); do { b = a % base; transform.push(b); a = a / base; } while(a != 0); transform.print(); return 0; }
发现上述两种方法只是实现了10进制向2进制的转换,不难发现很容易将上述方法应用到10进制向x进制的转换(x<=10),由于栈的类型是唯一的,若要扩展到10+x进制,还需要进行适当的改进。
对c的实现方法,只需将stackEle改为char,预先定义一个char m[] = "0123456789ABCDEF";,就可以解决问题了;对类的实现方法,也是只要做类似的改进。
2. 用栈实现一个小的计算器
主要是要考虑括号和优先级。将中序形式的输入转换为后序形式,也称为逆波兰表示(RPN)。
2.1 中序表达式转为后序表达式的直观方法
A:扫描中序表达式,碰到操作数(operand)后放入结果串中。
B:碰到操作符(operator)时,如果能确认其操作数,将操作数放入结果串中,否则,将操作符压入栈中。到表达式最后,将栈中的剩余操作符出栈。
拿a+b*c举例:首先a放入结果串中,+入栈,b放入结果串后结果成为ab,遇到*,*的优先级比+大,*入栈,这时栈中为+*,最后C放入结果串,成为abc,扫描到结尾了,将栈中操作符+*按LIFO的原则出栈,最终的结果为:abc*+
2.2 利用后序表示计算的方法
A:例如4+3*5,后序表示是4 3 5 * +:计算时采用这样的方法:
将操作数435依次压入栈
读到操作符*时,计算栈顶两操作数3 5在*下的结果,将结果15压入栈中,此时栈中元素为4 15
读取+,计算4+15,将结果19压入栈
扫描接触,19即是结果
B:类的设计
class postfixEval { public: postfixEval(); // default constructor. postfix expression is NULL string string getPostfixExp() const; // return the postfix expression void setPostfixExp(const string& postfixExp); // change the postfix expression int evaluate(); // evaluate the postfix expression and return // its value. the function throws expressionError // if an error occurs during evaluation private: string postfixExpression; // the postfix expression to evaluate stack<int> operandStack; // stack of operands void getOperands(int& left, int& right); // pop left and right operands from stack. // Precondition: the stack must have at least two entries. // if the stack is empty prior to a pop() operation, the // function throws the exception expressionError int compute(int left, int right, char op) const; // compute "left op right". if right is 0 and op // is '/' or '%', the function throws expressionError bool isOperator(char ch) const; // is ch one of '+','-','*','/','%','^' }; void postfixEval::getOperands(int& left, int& right) { // can we pop the right operand? if (operandStack.empty()) throw expressionError("postfixEval: Too many operators"); // pop right operand right = operandStack.top(); operandStack.pop(); // can we pop the left operand? if (operandStack.empty()) throw expressionError("postfixEval: Too many operators"); // pop left operand left = operandStack.top(); operandStack.pop(); } int postfixEval::compute(int left, int right, char op) const { int value; // evaluate "left op right" switch(op) { case '+': value = left + right; break; case '-': value = left - right; break; case '*': value = left * right; break; case '%': if (right == 0) throw expressionError("postfixEval: divide by 0"); value = left % right; break; case '/': if (right == 0) throw expressionError("postfixEval: divide by 0"); value = left / right; break; case '^': // make sure we are not computing 0^0 if (left == 0 && right == 0) throw expressionError("postfixEval: 0^0 undefined"); value = 1; // general case. compute value = 1*left*...*left. // if right == 0, skip the loop and left^0 is 1 while (right > 0) { value *= left; right--; } break; } return value; } bool postfixEval::isOperator(char ch) const { return ch == '+' || ch == '-' || ch == '*' || ch == '%' || ch == '/' || ch == '^'; } // default constructor postfixEval::postfixEval() {} string postfixEval::getPostfixExp() const { return postfixExpression; } void postfixEval::setPostfixExp(const string& postfixExp) { postfixExpression = postfixExp; } int postfixEval::evaluate() { // expValue contains the evaluated expression int left, right, expValue; char ch; int i; // process characters until the end of the string is reached // or an error occurs for (i=0; i < postfixExpression.length(); i++) { // get the current character ch = postfixExpression[i]; // look for an operand, which is a single digit // non-negative integer if (isdigit(ch)) // value of operand goes on the stack operandStack.push(ch - '0'); // look for an operator else if (isOperator(ch)) { // pop the stack twice and get the // left and right operands getOperands(left, right); // evaluate "left op right" and push on stack operandStack.push(compute(left, right, ch)); } // any other character must be whitespace. // whitespace includes blank, tab, and newline else if (!isspace(ch)) throw expressionError("postfixEval: Improper char"); } // the expression value is on the top of the stack. // pop it off expValue = operandStack.top(); operandStack.pop(); // if data remains on the stack, there are too // many operands if (!operandStack.empty()) throw expressionError("postfixEval: Too many operands"); return expValue; }
2.3中序表示式转后序
由于计算机器的面向对象是人,输入的时候不能让一个不会计算机的人输入他要求的式子的后序形式,因而必须设计一种算法,能够自动根据中序输入得到其相应的后序形式,这样,再调用上面的类进行计算时就可以得出结果了。
在中序转后序时,主要是要考虑运算符的个数、优先级(precedence)、结合性(associativity)等。
为了简化计算,仅对含二元操作符的表达式进行讨论。那么,对这样的表达式,若对操作数赋权1,操作符赋权-1,则一个正确输入的中序表达式的权值累加和应该为1,且在中间的任何一个累加点,累加和都应该介于0,1两值(因为二元操作符时,操作数的数目总比操作符数目多1)。
A 算法设计中应该要考虑到的问题
运算符优先级问题:如a*b+c,解决这个问题的方式是对运算符赋予栈优先级,将操作数添加到结果串中,运算符入栈,入栈时,如果栈顶元素的运算符优先级要大于当前元素的有限级,则栈顶元操作符要出栈,放入结果串中,例如当遇到+时,+<*,因而结果串为ab*,栈内为+,最后为ab*+。
解决^(指数计算符)等具有右结合性的元素的问题:如a^b^c=(a^(b^c))!=(a^b)^c,方法是给每个运算符再增加一个输入有限级,右结合性运算符的输入优先级大于其栈优先级,其它非右结合性运算符的输入优先级等于其栈优先级,这样,我们只要比较当前待压入栈的运算符的输入优先级和栈顶元素的栈优先级就可以确定一个运算符是否入栈的问题了,比如,如果新来操作符的栈优先级大于或等于栈顶运算符的栈优先级,则新来操作符入栈;否则,栈顶元素出栈。这将运算符的优先级的问题同时考虑进来了。
解决括号的问题:在原来的基础上,将左括号的输入优先级设为最大,栈优先级设为最小,这样,碰到左括号时,左括号总是要继续入栈,左括号后的操作符也总要继续入栈,剩下的规则沿用上面的,直到出现右括号时,才将左括号之前的操作符依次出栈,并丢弃左括号。
优先级设置:
符号 输入优先级 栈优先级 rank值
+ - 1 1 -1
*/% 2 2 -1
^ 4 3 -1
( 5 -1 0
) 0 0 0
B类的设计
// labels designating the parentheses characters const char lParen = '(', rParen = ')'; class infix2Postfix { public: infix2Postfix(); // default constructor. infix expression is NULL string infix2Postfix(const string& infixExp); // initialize the infix expression void setInfixExp(const string& infixExp); // change the infix expression string postfix(); // return a string that contains the equivalent postfix // expression. the function throws expressionError if an // error occurs during conversion private: string infixExpression; // the infix expression to convert string postfixExpression; // built to contain the postfix equivalent of infixExpression stack<expressionSymbol> operatorStack; // stack of expressionSymbol objects void outputHigherOrEqual(const expressionSymbol& op); // the expressionSymbol object op holds the current // symbol. pop the stack and output as long as the symbol // on the top of the stack has a precedence >= that of // the current operator bool isOperator(char ch) const; // is ch one of '+','-','*','/','%','^' }; void infix2Postfix::outputHigherOrEqual(const expressionSymbol& op) { expressionSymbol op2; while(!operatorStack.empty() && (op2 = operatorStack.top()) >= op) { operatorStack.pop(); postfixExpression += op2.getOp(); postfixExpression += ' '; } } bool infix2Postfix::isOperator(char ch) const { return ch == '+' || ch == '-' || ch == '*' || ch == '%' || ch == '/' || ch == '^'; } infix2Postfix::infix2Postfix() {} infix2Postfix::infix2Postfix(const string& infixExp): infixExpression(infixExp) {} void infix2Postfix::setInfixExp(const string& infixExp) { // assign a new infix expression infixExpression = infixExp; // make postfixExpression the NULL string postfixExpression = ""; } string infix2Postfix::postfix() { expressionSymbol op; // maintain rank for error checking int rank = 0, i; char ch; // process until end of the expression for (i=0; i < infixExpression.length(); i++) { ch = infixExpression[i]; // ******** process an operand ******** // an operand is a single digit non-negative integer if (isdigit(ch)) { // just add operand to output expression, followed by // a blank postfixExpression += ch; postfixExpression += ' '; // rank of an operand is 1, accumulated rank // must be 1 rank++; if (rank > 1) throw expressionError("infix2Postfix: Operator expected"); } // ********* process an operator or '(' ********** else if (isOperator(ch) || ch == '(') { // rank of an operator is -1. rank of '(' is 0. // accumulated rank should be 0 if (ch != '(') rank--; if (rank < 0) throw expressionError("infix2Postfix: Operand expected"); else { // output the operators on the stack with higher // or equal precedence. push the current operator // on the stack op = expressionSymbol(ch); outputHigherOrEqual(op); operatorStack.push(op); } } // ********* process a right parenthesis ********** else if (ch == rParen) { // build an expressionSymbol object holding ')', which // has precedence lower than the stack precedence // of any operator except '('. pop the operator stack // and output operators from the subexpression until // '(' surfaces or the stack is empty. if the stack is // empty, a '(' is missing; otherwise, pop off '('. op = expressionSymbol(ch); outputHigherOrEqual(op); if(operatorStack.empty()) throw expressionError("infix2Postfix: Missing '('"); else operatorStack.pop(); // get rid of '(' } // ********* make sure ch is whitespace ********** else if (!isspace(ch)) throw expressionError("infix2Postfix: Invalid input"); } // finish processing if (rank != 1) throw expressionError("infix2Postfix: Operand expected"); else { // flush operator stack and complete expression evaluation. // if find left parenthesis, a right parenthesis is // missing. while (!operatorStack.empty()) { op = operatorStack.top(); operatorStack.pop(); if (op.getOp() == lParen) throw expressionError("infix2Postfix: Missing ')'"); else { postfixExpression += op.getOp(); postfixExpression += ' '; } } } return postfixExpression; }
参考文献
[1] K&R The C Programming Language (the second edition)
[2] 《c++编程风格》(c++ programming style )作者 Tom Cargill 译者 聂雪军 机械工业出版社2007.1
[3] Data Structures with C++ using STL, 2nd Edition William H. Ford William R. Topp
类的设计程序来自文献[3]
相关文章推荐
- 堆栈的简单实现之二:进制转换方面的简单应用(C语言实现)
- 数据结构的应用——使用栈实现十进制到其他(2、8、16)进制的转换
- 数据结构的应用——使用栈实现十进制到其他(2、8、16)进制的转换
- YYMMDD转换成4位字符压缩表示算法java实现——应用各位不等进制的思想
- 栈的应用一:JavaScript实现数字进制间的相互转换
- 在.NET Framework中,实现各种进制的数值间的转换
- 琐记5:进制转换的程序实现
- 利用栈实现进制的转换!
- 新手学习数据结构与算法---堆栈的应用之进制转换
- 二进制数转换成十进制数(栈来实现)
- PHP实现十进制数字与二十六进制字母串相互转换操作示例
- PHP进制转换[实现2、8、16、36、64进制至10进制相互转换]
- 数制转换,将10进制转换为8进制 C语言 栈的应用
- perl实现16进制数转换成10进制数
- 巧用一条SQL 实现其它进制到十进制转换
- python基础--实现大数到任意进制的转换
- 不同进制数的转换及应用
- C++实现——任意进制之间的转换
- Python内置函数实现进制转换 - UncleDong - 博客园
- 数据结构练习:用顺序栈实现进制间的转换