栈应用之简易计算器算法的原理及实现(C语言)

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语言)_第1张图片

这时*号读入,栈顶元素比*号的优先级低,故没有输出,*号入栈。接着,c输出。
栈应用之简易计算器算法的原理及实现(C语言)_第2张图片
后面的符号是一个+号。从栈中弹出*号和+号并输出,因为它们的优先级不比+号更低。然后+号入栈。
栈应用之简易计算器算法的原理及实现(C语言)_第3张图片
读入“(”,它的优先级最后,入栈。d输出。
栈应用之简易计算器算法的原理及实现(C语言)_第4张图片
下面读到*号,除非正在处理“)”,否则“(”不会弹出。下面读入e,输出。
栈应用之简易计算器算法的原理及实现(C语言)_第5张图片
读入+号,将*号弹出并输出。+号入栈。然后读到f并输出。
栈应用之简易计算器算法的原理及实现(C语言)_第6张图片
现在,我们读到“)”,将栈元素弹出,直到“(”弹出。我们将+号输出。
栈应用之简易计算器算法的原理及实现(C语言)_第7张图片
下面又读到*号,入栈。然后,读入g并输出。
栈应用之简易计算器算法的原理及实现(C语言)_第8张图片
现在输入为空,将栈中所有元素弹出并输出。转化过程到此结束。
栈应用之简易计算器算法的原理及实现(C语言)_第9张图片
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 运行截图






你可能感兴趣的:(数据结构与算法分析(C语言))