Index |
1. 括号匹配(Balancing Symbols) 2. 中缀表达式转化为后缀表示式(Infix to Postfix Conversion) 3. 计算中缀表达式(Calculate Postfix Expression) 4. 计算后缀表达式(Calculate Expression -Dijkstra Algorithm) |
完整代码详见我的github(https://github.com/gnudennis/ds_c)(calculate-expression.h, calculate-expression.c calculate-expression-test.c)
Balancing Symbols
------------------------------------------------------------------------------------------------------------------------
思路:
----------------
做一个空栈.如果字符是开放符号,就将其压入栈中.如果字符是一个封闭符号,那么如果栈空,则出错;若栈不为空,则将栈元素弹出,如果弹出的符号不是对于的开放符号,则报错.最后,如果栈非空,也出错.
核心代码:
----------------
/* Balance symbols */
int
balance_symbols(const char *expression)
{
Stack symstk;
char c, top;
int current_success = 1;
symstk = stack_create(sizeof(char), NULL);
/* On-line Algorithm */
while ( (c = *expression++) != '\0' ) {
if ( c == '(' || c == '[' || c == '{')
stack_push(symstk, &c);
else if ( c == ')' || c == ']' || c == '}' ) {
if ( stack_is_empty(symstk) ) {
current_success = 0;
break;
}
stack_top(symstk, &top);
if ( c == ')' && top == '(' ||
c == ']' && top == '[' ||
c == '}' && top == '{'
) {
stack_pop(symstk);
} else {
current_success = 0;
break;
}
}
}
if ( !stack_is_empty(symstk) )
current_success = 0;
#if 0
if ( current_success)
fprintf(stdout, "Success...\n");
else
fprintf(stdout, "Fail...\n");
#endif
return current_success;
}
Infix to Postfix Conversion
------------------------------------------------------------------------------------------------------------------------
思路:
----------------
1.如果是操作数,直接输出. 3.如果是操作符,要保证当前操作符优先级最高(意思是:比我优先级高的肯定要先参与运算).即将栈顶元素逐个弹出直至当前操作符的优先级高于栈顶元素的优先级,然后入栈. -->这样做的目的是保证优先级高的操作符优先弹出. 例外:当遇到左括号必须停止.其实一对括号相当于一个整体一样. |
核心代码:
----------------
static int
priority(int c)
{
if ( c == '+' || c == '-' )
return 1;
if ( c == '*' || c == '/' )
return 2;
if ( c == '(' )
return 3;
fprintf(stderr, "Undefine this operator's priority\n");
exit(1);
}
static int
is_operator(int c)
{
return c == '+' || c == '-' ||
c == '*' || c == '/' || c == '(';
}
/* Convert infix expression to postfix expression */
void
infix2postfix(const char *infixexp, char *postfix)
{
Stack operator_stk;
char c = '(', top;
int is_still_digit = 0;
postfix[0] = '\0'; /* NULL in the beginning */
operator_stk = stack_create(sizeof(char), NULL);
/* Ensure the stack is not empty */
stack_push(operator_stk, &c);
while ( (c = *infixexp++) != '\0' ) {
if ( isdigit(c) || c == '.' ) { /* 1. operands */
if ( is_still_digit ) {
sprintf(postfix, "%s%c", postfix, c);
} else {
sprintf(postfix, "%s %c", postfix, c);
is_still_digit = 1;
}
} else {
is_still_digit = 0;
if ( c == ')' ) { /* 2. ')' */
while ( stack_top_and_pop(operator_stk, &top),
top != '(' ) {
sprintf(postfix, "%s %c", postfix, top);
}
/* Pop '(' */
// stack_pop(operator_stk);
} else if ( is_operator(c) ) {
for ( stack_top(operator_stk, &top);
top != '(' && priority(c) <= priority(top);
stack_top(operator_stk, &top)) {
sprintf(postfix, "%s %c", postfix, top);
stack_pop(operator_stk);
}
/* Push the current operator */
stack_push(operator_stk, &c);
}
}
}
while ( !stack_is_empty(operator_stk) ) {
stack_top(operator_stk, &top);
if ( top != '(' )
sprintf(postfix, "%s %c", postfix, top);
stack_pop(operator_stk);
}
}
分析:
----------------
1.操作数直接存放在postfix字符串中,而操作符必须经过操作符栈中,弹出才存入postfix字符串.
2.最开始的"stack_push(operator_stk,&c);"的目的是保证operator_stk非空.这样便于后续的统一处理.
开始加入一个"("其实也可以保证一对括号就是一个整体.
3. "top != '(' && priority(c) <=priority(top);"这个约束条件是保证:当前的操作符最高,但是遇到'('必须停止(相当于"()"相当于一个小整体).
为什么不是: "top!= '(' && priority(c) < priority(top);",可以这样理解,同级别操作符,在栈中的操作符在表达式中的位置先于当前操作符,即可看成"优先级更高",所以也要弹出.
Postfix Expressions
------------------------------------------------------------------------------------------------------------------------
Postfix Expressions很容易计算值.只需要将操作数入操作数栈,遇到操作符,就弹出两个操作数,使用该操作符进行运算.
核心代码:
----------------
static double
calculate(char operator, double left, double right)
{
double res;
switch ( operator ) {
case '+':
res = left + right;
break;
case '-':
res = left - right;
break;
case '*':
res = left * right;
break;
case '/':
res = left / right;
break;
default:
fprintf(stderr, "Undefined this operator\n");
exit(1);
}
return res;
}
/* Calculate postfix expression */
double
calc_postfix_expression(const char *postexp)
{
Stack operand_stk;
char c;
double partial_res = 0, exp = 0.1;
double left_operand, right_operand;
int is_still_digit = 0, is_after_punct = 0;
operand_stk = stack_create(sizeof(double), NULL);
while ( (c = *postexp++) != '\0' ) {
if ( isdigit(c) || c == '.' ) { /* operand: digit & '.' */
if ( !is_still_digit ) {
partial_res = 0;
is_still_digit = 1;
}
if ( c == '.' ) /* '.' */
is_after_punct = 1;
else {
if ( !is_after_punct )
partial_res = partial_res * 10 + (c - '0');
else {
partial_res += (c - '0') * exp;
exp *= 0.1;
}
}
} else { /* others */
if ( is_still_digit ) {
stack_push(operand_stk, &partial_res);
is_still_digit = 0;
is_after_punct = 0;
exp = 0.1;
}
if ( is_operator(c) ) {
stack_top_and_pop(operand_stk, &right_operand);
stack_top_and_pop(operand_stk, &left_operand);
partial_res = calculate(c, left_operand, right_operand);
stack_push(operand_stk, &partial_res);
}
}
}
stack_top_and_pop(operand_stk, &partial_res);
return partial_res;
}
Calculate Expression - Dijkstra Algorithm
------------------------------------------------------------------------------------------------------------------------
中缀表达式求值问题:可以使用 Dijkstra的双栈算法实现.结合Infix toPostfix Conversion和Postfix Expressions就可以解决.
思路:
----------------
建立两个栈,一个操作数栈,一个操作符栈. 1.如果是操作数,入操作数栈. 3.如果是操作符,要保证当前操作符优先级最高.即将操作符栈顶元素逐个弹出直至当前操作符的优先级高于操作符栈顶元素的优先级,并在操作数栈中取出两个操作数,进行运算,并将结果入操作数栈,再将当前操作符入操作符栈. 4.如果操作符栈非空,进一步将操作符取出,并取出操作数进行运算,并将结果入操作数栈. |
核心代码:
----------------
/* Calculate infix expression */
double
calc_infix_expression(const char *infixexp)
{
Stack operator_stk, operand_stk;
char c = '(', top;
double partial_res, exp = 0.1;
double left_operand, right_operand;
int is_still_digit = 0, is_after_punct = 0;
operand_stk = stack_create(sizeof(double), NULL);
operator_stk = stack_create(sizeof(char), NULL);
stack_push(operator_stk, &c); /* Ensure the stack is not empty */
while ( (c = *infixexp++) != '\0' ) {
if ( isdigit(c) || c == '.' ) { /* 1. operand: digit & '.' */
if ( !is_still_digit ) {
partial_res = 0;
is_still_digit = 1;
}
if ( c == '.' ) /* '.' */
is_after_punct = 1;
else { /* digit */
if ( !is_after_punct )
partial_res = partial_res * 10 + (c - '0');
else {
partial_res += (c - '0') * exp;
exp *= 0.1;
}
}
} else { /* others */
if ( is_still_digit ) {
stack_push(operand_stk, &partial_res);
is_still_digit = 0;
is_after_punct = 0;
exp = 0.1;
}
if ( c == ')' ) { /* 2: ')' */
while ( stack_top_and_pop(operator_stk, &top), top != '(' ) {
stack_top_and_pop(operand_stk, &right_operand);
stack_top_and_pop(operand_stk, &left_operand);
partial_res = calculate(top, left_operand, right_operand);
stack_push(operand_stk, &partial_res);
}
} else if ( is_operator(c) ) { /* 3: operators */
for ( stack_top(operator_stk, &top);
top != '(' && priority(c) <= priority(top);
stack_top(operator_stk, &top)) {
stack_top_and_pop(operand_stk, &right_operand);
stack_top_and_pop(operand_stk, &left_operand);
partial_res = calculate(top, left_operand, right_operand);
stack_push(operand_stk, &partial_res);
stack_pop(operator_stk);
}
/* Push the current operator */
stack_push(operator_stk, &c);
}
}
}
if ( is_still_digit ) {
stack_push(operand_stk, &partial_res);
is_still_digit = 0;
is_after_punct = 0;
exp = 0.1;
}
while ( !stack_is_empty(operator_stk) ) {
stack_top_and_pop(operator_stk, &top);
if ( top != '(' ) {
stack_top_and_pop(operand_stk, &right_operand);
stack_top_and_pop(operand_stk, &left_operand);
partial_res = calculate(top, left_operand, right_operand);
stack_push(operand_stk, &partial_res);
}
}
stack_top_and_pop(operand_stk, &partial_res);
return partial_res;
}
示例:
----------------