栈的应用 - 波兰式与逆波兰式

   上一篇笔记中,简单讲到了栈的特性,并且给出了栈的实现,现在要利用栈来实现表达式的求值问题。

   有三个名词与表达式的计算密切相关,分别是中缀表达式、前缀表达式和后缀表达式,它们都是表达式的一种记法,区别是运算符相对于操作数的位置不同:前缀表达式的操作符位于与其相关的操作数之前;中缀表达式的操作符位于与其相关的操作数之间;后缀表达式的操作符位于与其相关的操作数之后。(简而言之,前、中、后就是指操作符所在的位置)

   举例:

  • 中缀表达式:1+((2+3)*4)-5
  • 前缀表达式:-+1*+2345
  • 后缀表达式:123+4*+5-

   可以看到,中缀表达式其实就是人们常用的算术表达式的表示方法,为了正确表示运算顺序,可能会需要借用括号,人看起来清晰易懂,但是对于计算机来说,计算中缀表达式却是很复杂的。前缀表达式和后缀表达式则是计算机更易理解和计算的表示方法,因此计算表达式的值时,通常需要先将中缀表达式转换为前缀表达式或者后缀表达式,然后再进行求值。

   前缀表达式,又称波兰式,计算机计算前缀表达式的值时,需要借助一个栈,从右至左扫描表达式,遇到操作数时,直接将操作数入栈;遇到操作符时,从栈中弹出两个操作数,进行运算,然后将运算结果压入栈中;重复上述过程,直至扫描到表达式的最左端,最后运算得出的值即为所求表达式的值。

   后缀表达式,又称逆波兰式,计算机计算后缀表达式时,也需要借助一个栈,计算过程与前缀表达式类似,只是扫描表达式的顺序是从左至右。扫描过程中,遇到操作数,则将操作数直接压入栈中;遇到操作符时,从栈中弹出两个操作数,进行运算,然后将运算结果压入栈中;重复上述结果,直至扫描到表达式的最右端,最后运算得出的值即为所求表达式的值。

   注意:在进行运算时,操作数的顺序不能乱,不然对于除法或减法可能产生错误的结果。

将中缀表达式转换为后缀表达式

   将中缀表达式转换为后缀表达式遵循以下步骤:

  1. 初始化两个栈:运算符栈opStack存储中间结果的栈tempStack;
  2. 从左至右扫描中缀表达式;
  3. 遇到操作数时,将其压入tempStack;
  4. 遇到运算符时,需要比较其与opStack栈顶运算符的优先级:
    • 如果opStack为空,或栈顶运算符为左括号’(‘,则直接将此运算符压入opStack中;
    • 否则,如果优先级比栈顶运算符的,也直接将运算符压入opStack;
    • 否则,将opStack栈顶的运算符弹出并压入tempStack中,再次转入步骤4中与opStack中新的栈顶运算符进行比较;
  5. 遇到括号时:
    • 如果是左括号’(‘,则直接压入opStack;
    • 如果是右括号’)’,则依次弹出opStack栈顶的运算符,并压入tempStack中,直到遇到左括号为止,此时将这一对括号丢弃;
  6. 重复步骤2~5,直到表达式的最右边;
  7. 将opStack中剩余的运算符依次弹出并压入tempStack;
  8. 依次弹出tempStack中的元素保存到result中,然后将result逆序,所得的式子就是中缀表达式转换所得的后缀表达式。

将中缀表达式转换为前缀表达式

   将中缀表达式转换为前缀表达式遵循以下步骤:

  1. 初始化两个栈:运算符栈opStack和存储中间结果的栈tempStack;
  2. 从右至左扫描中缀表达式;
  3. 遇到操作数时,直接将其压入tempStack中;
  4. 遇到运算符时,比较其与opStack栈顶运算符的优先级:
    • 如果opStack为空,或者栈顶运算符为右括号’)’,则直接将此运算符压入opStack中;
    • 否则,若优先级比栈顶运算符的优先级高或相等,则直接将此运算符压入opStack中;
    • 否则,将opStack栈顶的运算符弹出并压入到tempStack中,再次转入步骤4,与opStack中新的栈顶运算符相比较;
  5. 遇到括号时:
    • 如果是右括号’)’,则直接压入opStack中;
    • 如果是左括号’(‘,则依次弹出opStack栈顶的运算符并压入tempStack中,知道遇到右括号’)’为止,此时将这一对括号丢弃;
  6. 重复步骤2~5,直到表达式的最左边;
  7. 将opStack中剩余的运算符依次弹出并压入tempStack中;
  8. 依次弹出tempStack中的元素保存到result中,result即为中缀表达式转换所得的前缀表达式。

代码实现

   以下给出利用栈实现中缀表达式转换为后缀表达式和前缀表达式的实现代码。(本来想把后缀表达式和前缀表达式的计算一起实现,但是由于栈的元素类型需要做一些修改,后续会补上。)

   代码中MyStack.hMyStack.cpp是栈的实现代码,与前一篇笔记一样;Expression.hExpression.cpp则给出了具体的转换函数toReversePolishNotation()toPolishNotation()test.cpp通过两个表达式,对函数进行了测试验证。

MyStack.h

#ifndef _MYSTACK_H_
#define _MYSTACK_H_

#include 
#include 
#include 
#include 
using namespace std;

#define INISIZE         2
#define INIINCREMENT    2

#define DYNAMIC

typedef char elementType;

class MyStack {
public:
    int top;
    int size;
    int increment;
    elementType * data;
    MyStack(int size = INISIZE, int increment = INIINCREMENT);
    ~MyStack();
    bool pop(elementType & e);
    bool push(elementType e);
    bool isEmpty();
    bool isFull();
    bool getTopElement(elementType & e);
    void printStack();      //打印栈中所有的元素,主要用于调试
};

#endif

MyStack.cpp

#include "MyStack.h"

MyStack::MyStack(int size, int increment)
{
    top = -1;
    this->size = size;
    this->increment = increment;
    data = (elementType *)malloc(sizeof(elementType) * this->size);
}

MyStack::~MyStack()
{
    if (data) {
        free(data);
    }
}

bool MyStack::pop(elementType & e) 
{
    if (isEmpty()) {
        e = -1;
        return false;
    } else {
        e = data[top];
        top --;
        return true;
    }
}

bool MyStack::push(elementType e)
{
    if (isFull()) {
#ifdef DYNAMIC
        size = size + increment;
        data = (elementType *)realloc(data, sizeof(elementType) * this->size);
        if (data == NULL) {
            return false;
        }
        top ++;
        data[top] = e;
        return true;
#else
        return false;
#endif
    } else {
        top ++;
        data[top] = e;
        return true;
    }
}

bool MyStack::isEmpty()
{
    return top == -1;
}

bool MyStack::isFull()
{
    return top == size - 1;
}

bool MyStack::getTopElement(elementType &e)
{
    if (isEmpty()) {
        e = -1;
        return false;
    } else {
        e = data[top];
        return true;
    }
}

void MyStack::printStack()
{
    int i;
    cout << "SN\t\telement" << endl; 
    for (i=0;i<=top;i++) {
        cout << i << "\t\t" << data[i] << endl;
    }
    cout << "total elements : " << top+1 << endl;
    cout << "stack size : " << size << endl;
}

Expression.h

#ifndef _EXPRESSION_H_
#define _EXPRESSION_H_

#include "MyStack.h"
#include 
#include 

/*
*  toPolishNotation: 将中缀表达式转换为前缀表达式
*  toReversePolishNotation: 将中缀表达式转换为后缀表达式
*/
char * toPolishNotation(const char* express);
char * toReversePolishNotation(const char* express);

#endif

Expression.cpp

#include "Expression.h"

/*
*  判断字符op是否是运算符
*/
bool isOperator(char op)
{
    return (op=='+')||(op=='-')||(op=='*')||(op=='/');
}

/*
*  获取运算符的优先级
*  +或- : 2
*  *或/ : 3
*  其它 : 1
*/
int getPriority(char op)
{
    switch(op) {
        case '+':
        case '-':
            return 2;
        case '*':
        case '/':
            return 3;
        default :
            return 1;
    }
}

/*
*  反转一个字符串,在转换为后缀表达式时用到
*/
void reverseStr(char *str)
{
    int i=0;
    int j=strlen(str)-1;
    char temp;
    while(i*/
char * toReversePolishNotation(const char * expression)
{
    if (expression == NULL) {
        return NULL;
    }
    int length = strlen(expression);
    MyStack opStack(length,0);
    MyStack tempStack(length,0);
    char * result = (char *)malloc(sizeof(char) * (length+1));
    const char * pstr = expression;
    char e,topE;
    int i=0;

    while(*pstr) {
        if (isdigit(*pstr)) {
            tempStack.push(*pstr);
        } else if (isOperator(*pstr)) {
            while (opStack.getTopElement(topE)) {
                if ((topE=='(') || (getPriority(*pstr)>getPriority(topE)))
                    break;
                opStack.pop(e);
                tempStack.push(e);
            }
            opStack.push(*pstr);
        } else if (*pstr == '(') {
            opStack.push(*pstr);
        } else if (*pstr == ')') {
            while (opStack.pop(e)) {
                if (e == '(')
                    break;
                tempStack.push(e);
            }
        }
        pstr ++;
    }
    while (opStack.pop(e)) {
        tempStack.push(e);
    }

    while (tempStack.pop(e)) {
        result[i] = e;
        i ++;
    }
    result[i] = '\0';
    reverseStr(result);

    return result;

}

/*
*  函数功能: 将中缀表达式转换为前缀表达式
*  参数: expression: 待转换的中缀表达式
*  返回值: 已经转换好的前缀表达式
*/
char * toPolishNotation(const char * expression)
{
    if (expression == NULL)
        return NULL;
    int length = strlen(expression);
    MyStack opStack(length,0);
    MyStack tempStack(length,0);
    char * result = (char *)malloc((length+1) * sizeof(char));
    char e,topE;
    int i=0;
    int index = length - 1;

    while (index >= 0) {
        if (isdigit(expression[index])) {
            tempStack.push(expression[index]);
        } else if (isOperator(expression[index])) {
            while (opStack.getTopElement(topE)) {
                if ((topE==')') || (getPriority(expression[index])>=getPriority(topE)))
                    break;
                opStack.pop(e);
                tempStack.push(e);
            }
            opStack.push(expression[index]);
        } else if (expression[index] == ')') {
            opStack.push(expression[index]);
        } else if (expression[index] == '(') {
            while (opStack.pop(e)) {
                if (e == ')') 
                    break;
                tempStack.push(e);
            }
        }
        index --;
    }

    while (opStack.pop(e)) {
        tempStack.push(e);
    }

    while (tempStack.pop(e)) {
        result[i] = e;
        i ++;
    }
    result[i] = '\0';

    return result;
}

test.cpp

#include "Expression.h"

int main()
{
    char expression[] = "1+((2+3)*4)-5";
    char ex[] = "1+2*5";
    char *result = toPolishNotation(expression);
    cout << "expression : " << expression << endl;
    cout << "Polish Notation : " << result << endl;
    result = toReversePolishNotation(expression);
    cout << "Reverse Polish Notation : " << result << endl;
    cout << endl;
    result = toPolishNotation(ex);
    cout << "expression : " << ex << endl;
    cout << "Polish Notation : " << result << endl;
    result = toReversePolishNotation(ex);
    cout << "Reverse Polish Notation : " << result << endl;
    cout << endl;
    return 0;
}

   代码测试结果:
栈的应用 - 波兰式与逆波兰式_第1张图片

你可能感兴趣的:(笔记摘抄)