1.用c和c++下用栈实现数的进制转换
method1: C的实现
#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++类的实现
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:类的设计
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类的设计
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]