1.后缀表达式
a * b
上面表达式称为中缀表达式,其特点是操作符位于中间位置(仅一个操作符)。
a b *
上面表达式称为后缀表达式,其特点是操作符位于后面位置(仅一个操作符)。
计算器算法的原理是将中缀表达式转换为后缀表达式,然后进行计算。我们在下一章节对中缀转后缀进行介绍。
表达式:6*(5+(2+3)*8+3)
其后缀表达式为:6 5 2 3 + 8 * + 3 + *
计算一个后缀表达式的原理:当见到一个数时就把他推入栈中;在遇到一个操作符时该操作符就作用于从该栈弹出的两个数上,将所得结果推入栈中。
现在演示上面的后缀表达式的计算过程。
前四个字符放入栈中:
下面读到+号,所以3和2从栈中弹出,并且它们的和5被压入栈中。
接着8入栈。
现在见到*号,8和5弹出,并且5 * 8 = 40入栈。
接着见到+号,40和5被弹出,并且5 + 40 = 45入栈。
3入栈。
然后见到+号,3和45弹出,45 + 3 = 48入栈。
见到*号,48和6弹出,48 * 6 = 288入栈。
操作符用完,栈中唯一的数就是最后的结果。
2.中缀到后缀的转换
将中缀表达式:
a + b * c + (d * e + f) * g
转化成后缀表达式:
a b c * + d e * f + g * +
其原理:当读到一个操作数的时候,立即把它放到输出中。操作符不立即输出,从而必须先存在栈中。当遇到左圆括号也要推入栈中。我们从一个空栈开始计算。
如果见到一个右括号,那么僵栈元素弹出,将弹出的符号写出直到我们遇到一个左括号。但这个左括号只被弹出,并不输出。
如果遇到其他符号,那么我们从栈中弹出栈元素直到发现优先级更低的元素为止。有一个例外:除非是在处理一个“)”,否则我们绝不弹出“(”。
最后,如果我们读到输入的末尾,我们将栈元素弹出直到该栈变成空栈,将符号写到输出中。
下面进行演示:
首先a输出,+号入栈,b输出。
这时*号读入,栈顶元素比*号的优先级低,故没有输出,*号入栈。接着,c输出。
后面的符号是一个+号。从栈中弹出*号和+号并输出,因为它们的优先级不比+号更低。然后+号入栈。
读入“(”,它的优先级最后,入栈。d输出。
下面读到*号,除非正在处理“)”,否则“(”不会弹出。下面读入e,输出。
读入+号,将*号弹出并输出。+号入栈。然后读到f并输出。
现在,我们读到“)”,将栈元素弹出,直到“(”弹出。我们将+号输出。
下面又读到*号,入栈。然后,读入g并输出。
现在输入为空,将栈中所有元素弹出并输出。转化过程到此结束。
3 算法的实现
算法封装在calculate.h中。
主要接口为:
int calculate(char* exp,double* res);
而内部接口:
int suffix(char* ch,char* res);
实现中缀转后缀的过程。
还有一个用宏函数实现的栈,放在Stack.h头文件中。因为C语言没有泛型,所以当需要使用多个类型的栈时,就需要使用宏函数。
3.1 calculate.h
//
// calculate.h
// Calculator
//
// Created by Wuyixin on 2017/5/27.
// Copyright © 2017年 Coding365. All rights reserved.
//
#ifndef calculate_h
#define calculate_h
#include
#include
#include "Stack.h"
#define OPERATOR_ROW (4)
#define OPERATOR_COLUMN (3)
#define TRUE (1)
#define FALSE (0)
#define EXPRESSION_SIZE (100)
static char operator_c[OPERATOR_ROW][OPERATOR_COLUMN] = {
{'+','-'},
{'*','/','%'},
{'^'},
{'(',')'}
};
/* 输入表达式,输出结果 */
int calculate(char* exp,double* res);
#endif /* calculate_h */
3.2 calculate.c
//
// calculate.c
// Calculator
//
// Created by Wuyixin on 2017/5/27.
// Copyright © 2017年 Coding365. All rights reserved.
//
#include "calculate.h"
#include
#include
#include
/* 生成用于存放char类型元素的栈 */
GENERIC_STACK(char, Char)
/* 生成用于存放Double类型元素的栈 */
GENERIC_STACK(double, Double)
#define SEPARATOR ' '
static int suffix(char* ch,char* res);
static double operater_func(double left,double right,char operater){
if (operater == '+')
return left + right;
else if (operater == '-')
return left - right;
else if (operater == '*')
return left * right;
else if (operater == '/'){
if (right == 0){
printf("除数不能为0!\n");
exit(EXIT_FAILURE);
}
return left / right;
}else if(operater == '%'){
return (int)left % (int)right;
}else if (operater == '^')
return pow(left, right);
return 0;
}
void input_error(){
printf("输入错误!\n");
// exit(EXIT_FAILURE);
}
/* 判断是否操作符 */
static int is_operater(int ch){
int size = OPERATOR_COLUMN * OPERATOR_ROW;
char* cur = operator_c[0];
while (cur <= operator_c[0] + size) {
if (*cur == ch) return TRUE;
cur++;
}
return FALSE;
}
/* 判断操作符ch1和ch2的优先级 如果ch1比ch2优先级高,返回值大于0;ch1和ch2优先级一样,返回值为0;ch1比ch2优先级低返回值小于0*/
static int priority(char ch1,char ch2){
char *cur;
int size,index1,index2;
cur = operator_c[0];
size = OPERATOR_COLUMN * OPERATOR_ROW;
index1 = index2 = 0;
for (int i = 0; i < size; i++) {
if (*cur == ch1)
index1 = i;
else if (*cur == ch2)
index2 = i;
cur++;
}
return index1/OPERATOR_COLUMN - index2/OPERATOR_COLUMN;
}
int calculate(char* exp,double* res){
char* str;
StackDouble S;
double num,left,right;
char exp_res[EXPRESSION_SIZE] = {0};
if ( suffix(exp, exp_res) == FALSE )
return FALSE;
printf("后缀表达式:%s\n",exp_res);
str = strtok(exp_res, " ");
S = CreateStackDouble(EXPRESSION_SIZE);
while(str != NULL){
/* 操作符 */
if (strlen(str) == 1 && is_operater(*str)){
/* 读到最后一个操作符的时候,栈里肯定有两个元素,否则就是输入错误 */
if (S->Top < 1){
input_error();
return FALSE;
}
else{
right = TopDouble(S);
PopDouble(S);
left = TopDouble(S);
PopDouble(S);
PushDouble(operater_func(left, right, *str), S);
}
}
/* 数字 */
else{
num = atof(str);
PushDouble(num, S);
}
str = strtok(NULL, " ");
}
/* 如果输入正确,最后入栈的就是最终的计算结果 */
if (!IsEmptyDouble(S)){
*res = TopDouble(S);
PopDouble(S);
}else{
input_error();
return FALSE;
}
DisposeStackDouble(S);
return TRUE;
}
/* 中缀表达式化为后缀表达式 */
static int suffix(char* ch,char* res){
StackChar S;
int prior,double_point = FALSE;
char operator,*temp;
temp = res;
S = CreateStackChar(EXPRESSION_SIZE);
while (*ch != '\0') {
/* 操作数放在输出中 */
if (isdigit(*ch)||*ch == '.'){
if (*ch == '.' ){
/* 一个数不能有两个小数点 */
if (double_point == TRUE){
input_error();
return FALSE;
}
double_point = TRUE;
}
*temp++ = *ch;
}
else if(is_operater(*ch)){
double_point = FALSE;
/* 使用空格分割 */
if (*ch != '(')
*temp++ = SEPARATOR;
if (IsEmptyChar(S))
PushChar(*ch, S);
else{
/* 读到右括号,一直弹栈到左括号为止 */
if (*ch == ')'){
do {
if (IsEmptyChar(S))
break;
operator = TopChar(S);
PopChar(S);
if (operator != '('){
*temp++ = operator;
*temp++ = SEPARATOR;
}
} while (operator != '(');
}else{
operator = TopChar(S);
prior = priority(*ch, operator);
/* 弹出栈顶元素,直到发现优先级更低的元素为止*/
if (prior > 0)
PushChar(*ch, S);
else{
do {
operator = TopChar(S);
if (operator == '(' || priority(*ch, operator) > 0)
break;
PopChar(S);
*temp++ = operator;
*temp++ = SEPARATOR;
} while (!IsEmptyChar(S));
PushChar(*ch, S);
}
}
}
}
else{
/* 除了空白字符以及不可打印字符之外,其他符号都是非法输入 */
if (!isblank(*ch)||!isprint(*ch)){
input_error();
return FALSE;
}
};
ch++;
}
/* 输入为空,将栈中的操作符全部弹出 */
while (!IsEmptyChar(S)){
operator = TopChar(S);
PopChar(S);
*temp++ = SEPARATOR;
*temp++ = operator;
}
DisposeStackChar(S);
return TRUE;
}
3.3 Stack.h
//
// Stack.h
// Calculator
//
// Created by Wuyixin on 2017/7/24.
// Copyright © 2017年 Coding365. All rights reserved.
//
#ifndef Stack_h
#define Stack_h
#include
#include
#define EmptyTOS (-1)
#define GENERIC_STACK( STACK_TYPE, SUFFIX ) \
\
typedef STACK_TYPE ElementType##SUFFIX; \
typedef struct StackRecord##SUFFIX *Stack##SUFFIX; \
\
struct StackRecord##SUFFIX{ \
int Capacity; \
int Top; \
ElementType##SUFFIX *Array; \
}; \
\
\
int IsEmpty##SUFFIX(Stack##SUFFIX S){ \
return S->Top == EmptyTOS; \
}\
\
\
int IsFull##SUFFIX(Stack##SUFFIX S){ \
return S->Top == S->Capacity - 1; \
}\
\
\
Stack##SUFFIX CreateStack##SUFFIX(int Capacity){ \
if (Capacity <= 0) \
return NULL; \
\
Stack##SUFFIX S = malloc(sizeof(struct StackRecord##SUFFIX)); \
\
if (S == NULL) \
exit(EXIT_FAILURE); \
\
S->Array = malloc(Capacity * sizeof(ElementType##SUFFIX)); \
if (S->Array == NULL) \
exit(EXIT_FAILURE); \
\
S->Capacity = Capacity; \
S->Top = EmptyTOS; \
\
return S; \
} \
\
void DisposeStack##SUFFIX(Stack##SUFFIX S){ \
\
if (S != NULL){ \
free(S->Array); \
free(S); \
} \
\
} \
\
\
void MakeEmpty##SUFFIX(Stack##SUFFIX S){ \
S->Top = EmptyTOS; \
} \
\
void Push##SUFFIX(ElementType##SUFFIX X,Stack##SUFFIX S){ \
if (!IsFull##SUFFIX(S)) \
S->Array[++S->Top] = X; \
else \
printf("栈已满!!!"); \
} \
\
ElementType##SUFFIX Top##SUFFIX(Stack##SUFFIX S){ \
return S->Array[S->Top]; \
} \
\
void Pop##SUFFIX(Stack##SUFFIX S){ \
if (!IsEmpty##SUFFIX(S)) \
S->Top--; \
}
#endif /* Stack_h */
3.4 main.c
//
// main.c
// Calculator
//
// Created by Wuyixin on 2017/5/27.
// Copyright © 2017年 Coding365. All rights reserved.
//
#include
#include
#include
#include
#include "calculate.h"
static jmp_buf state;
int main(int argc, const char * argv[]) {
char ch;
double res;
char *tmp, exp[EXPRESSION_SIZE] = {'\0'};
setjmp(state);
printf("请输入表达式(按C退出):\n");
tmp = exp;
/* 清空字符串 */
while (*tmp != '\0')
*tmp++ = '\0';
tmp = exp;
while ((ch = getchar()) != '\n') {
if (ch == 'C'||ch == 'c')
exit(0);
*tmp++ = ch;
}
if (calculate(exp, &res)){
printf("结果:%f\n",res);
}
longjmp(state, 1);
return 0;
}
3.5 运行截图