算法--逆波兰式(后缀式)

/****************************************************************************************************************
逆波兰式的作用:
  对于实现逆波兰式算法,难度并不大,但为什么要将看似简单的中序表达式转换为复杂的逆波兰式?
    原因就在于这个简单是相对人类的思维结构来说的,对计算机而言中序表达式是非常复杂的结构。
    相对的,逆波兰式在计算机看来却是比较简单易懂的结构。因为计算机普遍采用的内存结构是栈式结构,它执行先进后出的顺序。

如:我们平时写a+b,这是中缀表达式,写成后缀表达式就是:ab+
  (a+b)*c-(a+b)/e的后缀表达式为:如:我们平时写a+b,这是中缀表达式,写成后缀表达式就是:ab+
  (a+b)*c-(a+b)/e的后缀表达式为:
  (a+b)*c-(a+b)/e
  →((a+b)*c)((a+b)/e)-
  →((a+b)c*)((a+b)e/)-
  →(ab+c*)(ab+e/)-
  →ab+c*ab+e/-
  →ab+c*ab+e/-

算法实现: 

  将一个普通的中序表达式转换为逆波兰表达式的一般算法是:

  首先需要分配2个栈,一个作为临时存储运算符的栈S1(含一个结束符号),一个作为输入逆波兰式的栈S2(空栈),S1栈可先放入优先级最低的运算符#,
    注意,中缀式应以此最低优先级的运算符结束。可指定其他字符,不一定非#不可。从中缀式的左端开始取字符,逐序进行如下步骤:

  (1)若取出的字符是操作数,则分析出完整的运算数,该操作数直接送入S2栈;若取出的是运算符,并且当前S1栈顶为(,则当前运算符直接入S1栈。

  (2)若取出的字符是运算符,则将该运算符与S1栈栈顶元素比较,如果该运算符优先级大于S1栈栈顶运算符优先级,则将该运算符进S1栈,
        否者,将S1栈的栈顶运算符弹出,送入S2栈中,直至S1栈栈顶运算符低于(不包括等于)该运算符优先级,则将该运算符送入S1栈。

  (3)若取出的字符是“(”,则直接送入S1栈栈顶。

  (4)若取出的字符是“)”,则将距离S1栈栈顶最近的“(”之间的运算符,逐个出栈,依次送入S2栈,此时抛弃“(”。

  (5)重复上面的1~4步,直至处理完所有的输入字符

  (6)若取出的字符是“#”,则将S1栈内所有运算符(不包括“#”),逐个出栈,依次送入S2栈。

  完成以上步骤,S2栈便为逆波兰式输出结果。不过S2应做一下逆序处理。便可以按照逆波兰式的计算方法计算了!
*****************************************************************************************************************/ 

 

#include <stdio.h> 
#include <string.h>
#include <time.h>    /*记录运行时间*/ 
 
#include "栈的链式存储.c"

struct sNode *symbol;
struct sNode *result;
 
/* 1.符号优先级获取函数*/
int getPriority(char op)
{
    switch(op)
    {
        case '+':
        case '-':
            return 1;
        case '*':
        case '/':
            return 2;
        case '(':       /*不会有判断')'的情况*/ 
            return 0;        
        default:
            return -1; 
    }    
} 
/* 2.初始化栈*/
int init2stack()
{
    initStack(&symbol);
    initStack(&result);    
} 
/* 3.解析原始表达式,使之变成逆波兰式表达式(将表达式保存到result栈中,栈底为表达式左端).*/ 
void transform(char *str)
{
    elemType x,y;
    int i = 0; 
    int priority = -1;  
    
    /*遍历字符串*/ 
    while(i < strlen(str))   /*以换行符表示表达式结束,若表达式是通过外部输出,可更换*/
    {
            /*如果是操作数,直接压入result栈*/
            if(str[i] >= '0' && str[i]<='9')
            { 
                x = 0;
                while(str[i] >= '0' && str[i]<='9')
                {
                    x = x*10 + str[i] - 48;
                    i++;    
                }
                if(str[i] == '.')
                {
                    i++; 
                    y =0;
                    double j = 10.00; 
                    while(str[i] >= '0' && str[i]<='9')
                    {
                        y = y + str[i]/j; 
                        i++;    
                    }
                    x += y; 
                }
                push(&result,x); 
            }
            if(i >= strlen(str)) 
            {
                break;    
            } 
            /*如果是运算符 : 
                1.'('直接放入symbol栈
                2. ')'将symbol栈中最靠近栈顶的'(' 与栈顶见得运算符,从顶到底依次写入result栈.并pop出'('
                3. '-'或'+' 判断symbol栈顶运算符(小括号'('除外)的优先级,比'+-'小,就将'+-'压入栈symbol.比'+-'大于等于的,pop出来,压入result栈.
                    并将'+-'压入symbol栈
                4.'*'或'/'同第3条规则.
                5.若symbol栈为空,直接压栈 
            */ 
    
            switch(str[i])
            {
                case '(':
                    push(&symbol,'(');       
                    break;
                case ')':
                    while( (char)peek(&symbol) != '(')  /*double型的元素要强转一下*/ 
                    {
                        push(&result,pop(&symbol)); /*将( ) 之间的运算符导入result*/
                    }
                    pop(&symbol); 
                    break;
                case '+':
                case '-':
                case '*':
                case '/':
                    if(emptyStack(&symbol)) /*如果栈空,直接压栈*/ 
                    {
                        push(&symbol,str[i]);        
                    }else{  
                        priority = getPriority(str[i]);
                        if(priority <= getPriority((char)peek(&symbol))) /*若symbol栈顶元素优先级大,基本是栈顶为*'/',此时str[i]为+-的情况*/ 
                        {
                            push(&result,pop(&symbol));
                            push(&symbol,str[i]);    
                        }else{    /*栈顶为+-,str[i]为*'/'的情况*/                             
                            push(&symbol,str[i]);   
                        }                         
                    } 
                    break;
                default:
                   break;                            
            } 
            i++;
    } /*结束遍历原始表达式*/ 
    while(!emptyStack(&symbol)) /*将symbol中的所有剩余运算符送入result栈*/ 
    {
        push(&result,pop(&symbol)); 
    } 
} 
/* 4.使存储逆波兰式的栈中数据倒置,用于挨个取出进行分析计算(将result中表达式倒置入symbol栈中.表达式右端在栈顶).*/
void dataInversion()
{
    while(!emptyStack(&result))  /*将result中的逆波兰式倒置进symbol,以便进行最终结果计算时使用*/ 
    {
        push(&symbol,pop(&result)); 
    } 
} 
/* 5.分析计算函数*/ 
double compute()
{
    elemType x,y;         /*用于保存数字,x为整数部分,y为小数部分.(如果是小数的话)*/ 
    elemType temp; 
    int i;              /*用于遍历字符串*/
    
    while(emptyStack(&symbol) != 1)
    {
        temp = pop(&symbol);
        if((char)temp == ' ')  /*如果是空格,不做操作*/ 
        {
            i++;
            continue;    
        }
        
        switch((char)temp)
        {
            case '+':
                x = pop(&result);
                x = x + pop(&result); 
                i++; 
                break;
            case '-':
                x = pop(&result);
                x = pop(&result) - x;
                i++; 
                break;
            case '*':
                x = pop(&result);
                x = x * pop(&result);
                i++; 
                break;
            case '/':
                x = pop(&result);
                if(x>0.000001 && x<-0.000001)  /*不可以将浮点变量用“==”或者“!=”与任何数字比较。*/ 
                {
                    printf("除数是0,终止程序.\n");
                    system("pause");    
                }else{
                    x = pop(&result) / x;    
                } 
                break;
            default: 
                x = temp; 
                break;                                 
        } /*switch 判断当前字符结束*/ 
        push(&result,x);
    } /*str字符串遍历结束*/

   x = pop(&result); 
   return x; 
} 

/* 6.遍历整个栈,从底到顶*/
void travelStack(struct sNode *hs)
{
    struct sNode *temp = hs;
    char arr[100]; 
    int i,n =0; 
    
    while(temp != NULL)
    {
        arr[n++] = temp->data;       
        temp = temp->next; 
    }
    for(i = n-1; i>=0; i--)
    {
        switch((char)arr[i])
        {
            case '+':
            case '-':
            case '*': 
            case '/':
            case '(':
            case ')': 
                printf("%c ",(char)arr[i]);
                break;
            default: 
                printf("%d ",(int)arr[i]); 
                break;                     
        }          
    }    
}

/* 7.检查表达式格式,通过返回1,否则返回0*/ 
int checkExpression(char *str)
{
    int i = 0;
    int len = strlen(str);
    int head = 0;
    int parenthesis =0; 
    while(i < len)
    {
        /*如果是操作数,直接压入result栈*/
            if(str[i] >= '0' && str[i]<='9')
            { 
                while(str[i] >= '0' && str[i]<='9')
                {
                    i++;    
                }
                if(str[i] == '.')
                {
                    i++;  
                    while(str[i] >= '0' && str[i]<='9')
                    {
        
                        i++;    
                    } 
                }
                if(head == 0) head = 1;
                /*数字后面必须是+ - * '/' 或者,数字为表达式结尾时除外)*/ 
                if(str[i] != 42 && str[i] != 43 && str[i] != 45 && str[i] != 47 && i != len)
                {
                    if( str[i] != 41)
                    { 
                        printf("表达式错误... %c a附近\n",str[i]);
                        return 0;  
                    } 
                }  
            }
            if(i >= strlen(str)) 
            {
                return 1;    
            }
        switch(str[i])
        {
             
            case '+':
            case '-': 
            case '*': 
            case '/':
                /* '+''-''*''/'后面只可以是'('或者操作数,且不能在表达式开头,或结尾*/
                if(head == 0 || i== len -1)
                {
                    printf("表达式错误... %c 附近\n",str[i]);
                    return 0;  
                } 
                if((str[i+1] < '0'|| str[i+1] > '9' ))
                {
                    if(str[i+1] != '(')
                    { 
                        printf("表达式错误... %c 附近\n",str[i]);
                        return 0; 
                    } 
                } 
                break;                
            case '(':
                if(head == 0) head =1; 
                /* '+''-''*''/'后面只可以是'('或者操作数,且不能在表达式开头,或结尾*/
                if(i== len -1)
                {
                    printf("表达式错误... %c 附近\n",str[i]);
                    return 0;  
                }
                if(str[i+1] < '0' || str[i+1] > '9' )
                {
                    printf("表达式错误... %c 附近\n",str[i]);
                    return 0; 
                } 
                
                parenthesis++; 
                break;      
            case ')': 
                /* ')'后面只可以是运算符*/
                if(i == len -1) break; 
                if((str[i+1] != 42 && str[i+1] != 43 && str[i+1] != 45 && str[i+1] != 47) || head == 0) 
                {
                    printf("表达式错误... %c 附近\n",str[i]);
                    return 0; 
                } 
                
                parenthesis--;
                if(parenthesis < 0)
                {
                    printf("表达式错误... 缺少与 ) 匹配的 (\n");
                    return 0;         
                } 
                break;
            default:
                printf("不明运算符: %c\n",str[i]);
                return 0;  
        }
        i++;    
    }
    if(parenthesis < 0)
    {
            printf("表达式错误... 缺少与 ) 匹配的 (\n");
            return 0;         
    }else if(parenthesis > 0)  
    {
            printf("表达式错误... 缺少与 ( 匹配的 )\n");
            return 0;
    } 
    
    return 1; 
}
 
int main(void)
{
    double cost_time; 
    clock_t start,end; /*时间毫秒级*/ 
    char *str = "(1+2)*3-(5+6)/4";
    elemType res; 
    
    start=clock();
    printf("表达式为:\n\t%s\n",str);
    printf("正在检查表达式格式...\n"); 
    if(checkExpression(str) == 0)
    {        
       system("pause");         
    } 
    printf("将表达式转换成逆波兰式(后缀式)...\n\t"); 
    transform(str); 
    travelStack(result); 
    
    printf("\n进行倒置\n\t"); 
    dataInversion(); 
    travelStack(symbol); 
    
    res = compute(); 
    printf("\n结果为 : %f\n",res); 
    end=clock();
    cost_time=(double)(end-start)/CLOCKS_PER_SEC; 
    printf("所用时间: %f\n",cost_time);
    system("pause"); 
    return 0;   
} 

******************************************************************************

运行结果:

******************************************************************************

表达式为:
        (1+2)*3-(5+6)/4
正在检查表达式格式...
将表达式转换成逆波兰式(后缀式)...
        1 2 + 3 * 5 6 + 4 / -
进行倒置
        - / 4 + 6 5 * 3 + 2 1
结果为 : 6.250000
所用时间: 0.002000
请按任意键继续. . .

 

 

你可能感兴趣的:(算法)