中缀表达式求值问题

                          
中缀表达式求值问题

  中缀表达式的求值问题是一个比较常见的问题之一,我们通常在编写程序时,直接写出表达式让编译器去处理,很少去关心编译器是怎么对表达式进行求值的,今天我们来一起了解一下其中具体的原理和过程。

  表达式一般来说有三种:前缀表达式、中缀表达式、后缀表达式,其中后缀表达式又叫做逆波兰表达式。中缀表达式是最符合人们思维方式的一种表达式,顾名思义,就是操作符在操作数的中间。而前缀表达式和后缀表达式中操作符分别在操作数的前面和操作数的后面。举个例子:

  3+2

  这个是最简单的一个中缀表达式。而其等同的前缀表达式形式为+32,后缀表达式形式为32+。

  那么一些朋友可能会问既然中缀表达式最符合人类的思维习惯,为什么还需要前缀表达式和后缀表达式?先看一个例子,假如在前面的表达式基础上加一点东西:

  3+2*5

  此时的表达式很显然,如果进行计算,则先计算2*5,最后计算加法。但是如果需要先计算加法运算呢?则必须加上括号,(3+2)*5。

  而如果用后缀表达式来表示,则为 32+5*,那么该表达式的计算顺序为3+2 —>
(3+2)*5。

  区别就在这里,后缀表达式不需要用括号就能表示出
整个表达式哪部分运算先进行。同理,前缀表达式也是如此。这种表达式正好最符合计算机的处理方式,因为后缀表达式和前缀表达式求值不需要考虑优先级的问题,计算机处理起来便简单很多。

  今天我们这里主要讲解中缀表达式和后缀表达式(前缀表达式和后缀表达式很类似,就不做过多赘述),下面是讲解大纲:

  • 中缀表达式如何直接求值?

  • 后缀表达式如何直接求值?

  • 中缀表达式如何转换为后缀表达式?

1.中缀表达式直接求值

  对于中缀表达式求值来说,一般最常见的直接解决办法就是利用栈,一个栈用来保存操作数,一个栈用来保存操作符。

  为了简便起见,暂时表达式中只考虑简单的+,-,*,/运算,只有圆括号,并且都是整数。

  假如有这样一个表达式:((3+5*2)+3)/5+6/4*2+3

  对于这样一个表达式,如果让你来设计操作数和操作符进栈的出栈的规则,你会怎么设计?

  先不看这么复杂的表达式,考虑一下简单点的,还是前面的3+2*5,那么很显然先进行乘法运算,后进行加法运算,但是由于操作符在操作数中间,所以当一个操作符进操作符栈时,该操作符的两个操作数并没有都进入到操作数栈中,那么如何解决呢?只有在后面一个操作符进操作符栈时,前面的一个操作符所作用的两个操作数才会全部进栈。比如3+2*5,栈的变化过程为:

  操作数栈:3     
操作数栈:3   操作数栈:3 2

  操作符栈:空     操作符栈:+ 
操作符栈:+

  注意此时遇到操作符“*”,是不是需要弹出操作数栈中的两个操作数进行运算呢,很显然不是,因为乘法运算法比操作符栈的栈顶运算符优先级高,也就是说当前的操作符在“+”前进行运算,那么还需要将当前操作符压栈,则变成:

  操作数栈:3
2   操作数栈:3 2 5

  操作符栈:+
*  操作符栈:+ *

  此时到了表达式的结尾,既然栈顶的操作符的优先级比栈底的操作符的优先级高,那么可以取操作符栈的栈顶操作符和操作数栈的栈顶两个元素进行计算,则得到2*5=10,(注意从操作数栈先弹出的操作数为右操作数)。此时得到10
,则应该把10继续压到操作数栈中,继续取操作符栈的栈顶操作符,依次进行下去,则当操作符栈为空时表示计算过程完毕,此时操作数栈中剩下的唯一元素便是整个表达式的值。

  再换个例子:2*5+3,这个表达式跟前面表达式的结果虽然相同,但是操作数和操作符入栈和出栈的顺序发生了很大变化:

  操作数栈:2    
操作数栈:2   操作数栈:2 5

  操作符栈:空    操作符栈:* 
 操作符栈:*

  此时遇到“+”,而操作符栈的栈顶操作符为“*”,栈顶操作符优先级更高,表示此时可以取操作符栈顶操作符进行运算,那么栈变成:

  操作数栈:10   操作数栈:10 3

  操作符栈:空    操作符栈:+

  后面的过程跟前面一个例子类似。

  如果复杂一点,比如包含有括号,连续的乘除法这些怎么处理呢?道理是一样的,对于左括号直接入栈,碰到右括号,则一直将操作符退栈,直到碰到左括号,则括号中的表达式计算完毕。对于连续的乘除法,跟前面例子中处理过程类似。只需要记住一点:只有当前操作符的优先级高于操作符栈栈顶的操作符的优先级,才入栈,否则弹出操作符以及操作数进行计算直至栈顶操作符的优先级低于当前操作符,然后将当前操作符压栈。当所有的操作符处理完毕(即操作符栈为空时),操作数栈中剩下的唯一一个元素便是最终的表达式的值。而操作符的优先级为:+和-优先级是一样的,*和/优先级是一样的,+、-的优先级低于*、/的优先级。

  不过需要注意的是在求值之前需要对表达式进行预处理,去掉空格、识别
负号(区分“-”是作为减号还是负号),提取操作数等。

  对于“-”的区分,主要判别方法为:

  1)若前一个字符为‘(‘,则必定为负号;

  2)若前一个字符为‘)‘或者数字,则必定为减号;

  3)若前面一个字符为其他运算符,如*,/,则必定是负号;

  3)若前面没有字符,即该字符为表达式的第一个字符,则必定是负号。

  也就是说只有一种情况下,”-“是作为减号使用的,就是前一个字符为‘)‘或者数字的时候。

  如果判断出“-”是作为负号使用的,这里我采用“#”来代替“-”,并将其作为一种运算(优先级最高)。比如:-3*2

  我采取的做法是将"#"入栈,然后当遇到“*”时,由于栈顶操作符为"#",因此取#,然后取操作数栈的栈顶元素(只取一个)进行运算,然后再把结果压栈。

  下面是具体实现:

?





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

/*2014.5.6 测试环境: mingw*/

#include <iostream>

#include <vector>

#include <stack>

#include <string>

#include <cstdlib>

#include <cctype>

#include <cstring>

using
namespace std;

vector<string> preParse(char
*str)   //对中缀表达式进行预处理,分离出每个token

{

    vector<string> tokens;

    int
len = strlen(str);

    char
*p = (char
*)malloc((len+1)*sizeof(char));  //注意不要用 char *p = (char *)malloc(sizeof(str))来申请空间

    int
i=0,j=0;

    while(i<len)          //去除表达式中的空格

    {

        if(str[i]==‘ ‘)

        {

            i++;

            continue;

        }

        p[j++] = str[i++];

    }

    p[j]=‘\0‘;

    j=0;

    len = strlen(p);

    while(j<len)

    {

        char
temp[2];

        string token;

        switch(p[j])

        {

            case
‘+‘:

            case
‘*‘:

            case
‘/‘:

            case
‘(‘:

            case
‘)‘:

                {

                    temp[0] =p[j];

                    temp[1] = ‘\0‘;

                    token=temp;

                    tokens.push_back(token);

                    break;

                }

            case
‘-‘:

                {

                    if(p[j-1]==‘)‘||isdigit(p[j-1]))  //作为减号使用

                    {

                        temp[0] =p[j];

                        temp[1] = ‘\0‘;

                        token=temp;

                        tokens.push_back(token);

                    }

                    else   
//作为负号使用

                    {

                        temp[0] =‘#‘;

                        temp[1] = ‘\0‘;

                        token=temp;

                        tokens.push_back(token);

                    }  

                    break;

                }

            default:     //是数字

                {

                    i = j;

                    while(isdigit(p[i])&&i<len)

                    {

                        i++;

                    }

                    char
*opd = (char
*)malloc(i-j+1);

                    strncpy(opd,p+j,i-j);

                    opd[i-j]=‘\0‘;

                    token=opd;

                    tokens.push_back(token);

                    j=i-1;

                    free(opd);

                    break;

                }  

        }

        j++;

    }

    free(p);

    return
tokens;

}

int
getPriority(string opt)

{

    int
priority;

    if(opt=="#")

        priority = 3;  

    else
if(opt=="*"||opt=="/")

        priority = 2;

    else
if(opt=="+"||opt=="-")

        priority = 1;

    else
if(opt=="(")

        priority = 0;

    return
priority;

}

void
calculate(stack<int> &opdStack,string opt)

{

    if(opt=="#"//进行负号运算

    {

        int
opd = opdStack.top();

        int
result = 0-opd;

        opdStack.pop();

        opdStack.push(result);

        cout<<"操作符:"<<opt<<" "<<"操作数:"<<opd<<endl; 

    }

    else
if(opt=="+")

    {

        int
rOpd = opdStack.top();

        opdStack.pop();

        int
lOpd = opdStack.top();

        opdStack.pop();

        int
result = lOpd + rOpd;

        opdStack.push(result); 

        

        cout<<"操作符:"<<opt<<" "<<"操作数:"<<lOpd<<" "<<rOpd<<endl; 

    }

    else
if(opt=="-")

    {

        int
rOpd = opdStack.top();

        opdStack.pop();

        int
lOpd = opdStack.top();

        opdStack.pop();

        int
result = lOpd - rOpd;

        opdStack.push(result); 

        cout<<"操作符:"<<opt<<" "<<"操作数:"<<lOpd<<" "<<rOpd<<endl; 

    }

    else
if(opt=="*")

    {

        int
rOpd = opdStack.top();

        opdStack.pop();

        int
lOpd = opdStack.top();

        opdStack.pop();

        int
result = lOpd * rOpd;

        opdStack.push(result); 

        cout<<"操作符:"<<opt<<" "<<"操作数:"<<lOpd<<" "<<rOpd<<endl; 

    }

    else
if(opt=="/")

    {

        int
rOpd = opdStack.top();

        opdStack.pop();

        int
lOpd = opdStack.top();

        opdStack.pop();

        int
result = lOpd / rOpd;

        opdStack.push(result); 

        cout<<"操作符:"<<opt<<" "<<"操作数:"<<lOpd<<" "<<rOpd<<endl; 

    }

}

int
evaMidExpression(char
*str)   //中缀表达式直接求值

{

    vector<string> tokens = preParse(str);

    int
i=0;

    int
size = tokens.size();

    

    stack<int> opdStack;     //存储操作数

    stack<string> optStack;   //存储操作符

    for(i=0;i<size;i++)

    {

        string token = tokens[i];

        if(token=="#"||token=="+"||token=="-"||token=="*"||token=="/")

        {

            if(optStack.size()==0)   //如果操作符栈为空

            {

                optStack.push(token);

            }

            else

            {

                int
tokenPriority = getPriority(token);

                string topOpt = optStack.top();

                int
topOptPriority = getPriority(topOpt);

                if(tokenPriority>topOptPriority)

                {

                    optStack.push(token);

                }

                else

                {

                    while(tokenPriority<=topOptPriority)

                    {

                        optStack.pop();

                        calculate(opdStack,topOpt);

                        if(optStack.size()>0)

                        {

                            topOpt = optStack.top();

                            topOptPriority = getPriority(topOpt);

                        }

                        else

                            break;

                        

                    }

                    optStack.push(token);

                }

            }  

        }

        else
if(token=="(")

        {

            optStack.push(token);

        }

        else
if(token==")")

        {

            while(optStack.top()!="(")

            {

                string topOpt = optStack.top();

                calculate(opdStack,topOpt);

                optStack.pop();

            }

            optStack.pop();

        }

        else  
//如果是操作数,直接入操作数栈

        {

            opdStack.push(atoi(token.c_str()));

        }

    }

    while(optStack.size()!=0)

    {

        string topOpt = optStack.top();

        calculate(opdStack,topOpt);

        optStack.pop();

    }

    return
opdStack.top();

}

int
main(int
argc, char
*argv[])

{

    char
*str = "((3+5*2)+3)/5+(-6)/4*2+3";

    cout<<evaMidExpression(str)<<endl;

    return
0;

}

  运行结果:

  

2.后缀表达式直接求值

  由于后缀表达式不需要用括号来表示计算顺序,因此处理起来比较简单,具体的可以参照:

  http://www.cnblogs.com/dolphin0520/p/3708587.html

3.中缀表达式如何转为后缀

  大部分数据结构教材在讲述
栈的时候都会涉及到中缀表达式转为后缀表达式的问题,因为这个是栈的典型应用之一。因此很多教材上都会利用栈来进行转换,这里我们来讨论一下最常见的两种转换思路和一种简便的验证方法。

  • 利用二叉树进行转换

  由于二叉树本身结构的特殊性,使得我们可以利用它来很轻松地将中缀表达式转变成后缀表达式,事实上,只要根据中缀表达式建立好相应的二叉树之后,直接对二叉树进行前序遍历和后序遍历便可得到前缀表达式和后缀表达式。在利用二叉树来表示表达式时,用叶子节点来存储操作数,用内部节点存储操作符,比如这样一个表达式3*5+5/2+(3+5)*2,表示成二叉树的形式(注意其有等同的其他形式)就是:

  

  其实讲中缀表达式的过程转变成二叉树的形式是一个递归的过程,比如有一个表达式,其对应的的二叉树的根节点必定是优先级最低的操作符(也就是说是整个表达式中最后进行的运算操作),然后再在操作符的左部分中找出最后进行的操作符作为根节点的左孩子,在操作符的右部分中找出最后进行的操作符作为根节点的右孩子,然后知道左部分或者右部分是单纯的操作数,则作为叶子节点,直到整个二叉树建立完毕。

  下面是具体实现:

  参考了一下这篇博文的实现,但是这篇博文没有考虑减号作为负号使用的情况。

  http://blog.csdn.net/ericming200409/article/details/5919883

  

?





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

/*

测试环境:VS2010

*/

#include <iostream>

#include <string>

#include <cctype>

#include <cstring>

#include <cstdlib>

#include <queue>

using
namespace std;

typedef
struct node

{

    struct
node *left;

    struct
node *right;

    char
*data;

}BinTree;

char
* preProcess(char
*str)   //预处理,除去空格,将负号替代为#

{

    int
len = strlen(str);

    char
*p = (char
*)malloc(sizeof(char)*len);

    int
i=0,j=0;

    while(i<len)          //去除表达式中的空格

    {

        if(str[i]==‘ ‘)

        {

            i++;

            continue;

        }

        p[j++] = str[i++];

    }

    p[j]=‘\0‘;

    j=0;

    len = strlen(p);

    while(j<len)

    {

        if(p[j]==‘-‘)

        {

            if(!(p[j-1]==‘)‘||isdigit(p[j-1])))  //作为减号使用

            {

                p[j]=‘#‘;  

            }

        }

        j++;

    }

    return
p;

}

/*

最后执行的操作符一定是在括号外面,也就是说brackets一定是等于0的,

*/

int
indexOfOpt(char
*str,int
begin ,int
end)   //寻找最后执行的操作符的下标

{

    int
i;

    int
brackets=0;                    //所在括号层次

    int
index = -1;

    int
existAddOrMinus = 0;

    int
existMulOrDevide = 0;

    while(str[begin]==‘(‘&&str[end]==‘)‘)   //去除最外层的括号

    {

        begin++;

        end--;

    }

    for(i=begin;i<=end;i++)

    {

        if(str[i]==‘(‘)

            brackets++;

        else
if(str[i]==‘)‘)

            brackets--;

        else
if((str[i]==‘+‘||str[i]==‘-‘)&&brackets==0)

        {

            index = i;

            existAddOrMinus = 1;    //存在加减号

        }

        else
if((str[i]==‘*‘||str[i]==‘/‘)&&brackets==0&&existAddOrMinus==0)

        {

            index = i;

            existMulOrDevide = 1;  //存在乘除号

        }

        else
if(str[i]==‘#‘&&brackets==0&&existAddOrMinus==0&&existMulOrDevide==0)  //用‘#‘代表负号

        {

            index = i;

        }

    }

    return
index;

}

BinTree * createBinTree(char
*str,int
begin,int
end)

{

    BinTree *p =(BinTree *)malloc(sizeof(BinTree));;

    int
index = indexOfOpt(str,begin,end);

    cout<<"index:"<<index<<endl; 

    if(index==-1)   //表示只有操作数了

    {

        while(str[begin]==‘(‘&&str[end]==‘)‘)

        {

            begin++;

            end--;

        }

        p->data = (char
*)malloc(sizeof(end-begin+2));

        int
i,j=0;

        for(i=begin;i<=end;i++) 

            p->data[j++] = str[i];

        p->data[j]=‘\0‘;

        p->left = NULL;

        p->right = NULL;

        cout<<"操作数:"<<p->data<<endl;

    }

    else

    {

        p->data = (char*)malloc(2);

        p->data[0] = str[index];

        p->data[1]=‘\0‘;

        cout<<"操作符:"<<p->data<<endl;

            

        while(str[begin]==‘(‘&&str[end]==‘)‘)

        {

            begin++;

            end--;

        }

        if(str[index]==‘#‘//是负号

        {

            p->left = NULL;

        }

        else

        {

            p->left = createBinTree(str,begin,index-1);

        }

        p->right = createBinTree(str,index+1,end);

    }

    return
p;

}

void
preOrder(BinTree *root)

{

    if(root!=NULL)

    {

        cout<<root->data<<" ";

        preOrder(root->left);

        preOrder(root->right);

    }

}

void
inOrder(BinTree *root)

{

    if(root!=NULL)

    {

        inOrder(root->left);

        cout<<root->data<<" ";

        inOrder(root->right);

    }

}

void
postOrder(BinTree *root)

{

    if(root!=NULL)

    {

        postOrder(root->left);

        postOrder(root->right);

        cout<<root->data<<" ";

    }

}

int
main(void)

{

    char
*str = "((3+5*2)+3)/5+(-6)/4*2+3";

    char
*newStr = preProcess(str);

    cout<<newStr<<endl;

    BinTree *root=createBinTree(newStr,0,strlen(newStr)-1);

    inOrder(root);

    cout<<endl;

    postOrder(root);

    cout<<endl;

    system("pause");

    return
0;

}

  上述代码在VS2010下运行是没有问题的,但是在gcc下编译运行会崩溃,调试了很久没发现原因(如果有哪位朋友知道原因的请麻烦告知)。测试结果:

  • 利用栈进行转换

  利用栈进行转换的思路其实跟前面直接对中缀表达式求值的过程类似,在这过程中需要一个栈用来保存操作符optStack,需要一个数组用来保存后缀表达式suffix[],然后从头到尾扫描表达式

  1)如果遇到操作符,则跟optStack的栈顶操作符比较优先级,如果大于栈顶操作符的优先级,则入栈,否则不断取栈顶操作符加到suffix的末尾,直到栈顶操作符优先级低于该操作符,然后将该操作符入栈;

  2)遇到操作数,直接加到suffix的末尾

  3)遇到左括号,入栈;

  4)遇到右括号,则依次弹出栈顶操作符加到suffix的末尾,直到遇到左括号,然后将左括号出栈。

  具体实现:

?





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

/*2014.5.6 测试环境: mingw*/

#include <iostream>

#include <vector>

#include <stack>

#include <string>

#include <cstdlib>

#include <cctype>

#include <cstring>

using
namespace std;

vector<string> preParse(char
*str)   //对中缀表达式进行预处理,分离出每个token

{

    vector<string> tokens;

    int
len = strlen(str);

    char
*p = (char
*)malloc((len+1)*sizeof(char));  //注意不要用 char *p = (char *)malloc(sizeof(str))来申请空间

    int
i=0,j=0;

    while(i<len)          //去除表达式中的空格

    {

        if(str[i]==‘ ‘)

        {

            i++;

            continue;

        }

        p[j++] = str[i++];

    }

    p[j]=‘\0‘;

    j=0;

    len = strlen(p);

    while(j<len)

    {

        char
temp[2];

        string token;

        switch(p[j])

        {

            case
‘+‘:

            case
‘*‘:

            case
‘/‘:

            case
‘(‘:

            case
‘)‘:

                {

                    temp[0] =p[j];

                    temp[1] = ‘\0‘;

                    token=temp;

                    tokens.push_back(token);

                    break;

                }

            case
‘-‘:

                {

                    if(p[j-1]==‘)‘||isdigit(p[j-1]))  //作为减号使用

                    {

                        temp[0] =p[j];

                        temp[1] = ‘\0‘;

                        token=temp;

                        tokens.push_back(token);

                    }

                    else   
//作为负号使用

                    {

                        temp[0] =‘#‘;

                        temp[1] = ‘\0‘;

                        token=temp;

                        tokens.push_back(token);

                    }  

                    break;

                }

            default:     //是数字

                {

                    i = j;

                    while(isdigit(p[i])&&i<len)

                    {

                        i++;

                    }

                    char
*opd = (char
*)malloc(i-j+1);

                    strncpy(opd,p+j,i-j);

                    opd[i-j]=‘\0‘;

                    token=opd;

                    tokens.push_back(token);

                    j=i-1;

                    free(opd);

                    break;

                }  

        }

        j++;

    }

    free(p);

    return
tokens;

}

int
getPriority(string opt)

{

    int
priority;

    if(opt=="#")

        priority = 3;  

    else
if(opt=="*"||opt=="/")

        priority = 2;

    else
if(opt=="+"||opt=="-")

        priority = 1;

    else
if(opt=="(")

        priority = 0;

    return
priority;

}

vector<string> toSuffix(char
*str)  //转变为后缀形式

{

    vector<string> tokens = preParse(str);

    int
i=0;

    int
size = tokens.size();

    

    vector<string> suffix;     //存储后缀表达式

    stack<string> optStack;   //存储操作符

    

    for(i=0;i<size;i++)

    {

        string token = tokens[i];

        if(token=="#"||token=="+"||token=="-"||token=="*"||token=="/")

        {

            if(optStack.size()==0)   //如果操作符栈为空

            {

                optStack.push(token);

            }

            else

            {

                int
tokenPriority = getPriority(token);

                string topOpt = optStack.top();

                int
topOptPriority = getPriority(topOpt);

                if(tokenPriority>topOptPriority)

                {

                    optStack.push(token);

                }

                else

                {

                    while(tokenPriority<=topOptPriority)

                    {

                        optStack.pop();

                        suffix.push_back(topOpt);

                        if(optStack.size()>0)

                        {

                            topOpt = optStack.top();

                            topOptPriority = getPriority(topOpt);

                        }

                        else

                            break;

                        

                    }

                    optStack.push(token);

                }

            }  

        }

        else
if(token=="(")

        {

            optStack.push(token);

        }

        else
if(token==")")

        {

            while(optStack.top()!="(")

            {

                string topOpt = optStack.top();

                suffix.push_back(topOpt);

                optStack.pop();

            }

            optStack.pop();

        }

        else  
//如果是操作数,直接入操作数栈

        {

            suffix.push_back(token);

        }

    }

    while(optStack.size()!=0)

    {

        string topOpt = optStack.top();

        suffix.push_back(topOpt);

        optStack.pop();

    }

    return
suffix;

}

int
main(int
argc, char
*argv[])

{

    char
*str = "((3+5*2)+3)/5+(-6)/4*2+3";

    vector<string> suffix = toSuffix(str);

    int
size = suffix.size();

    for(int
i=0;i<size;i++)

        cout<<suffix[i]<<" ";

    cout<<endl;

    return
0;

}

  测试结果:

  • 简便验证办法

  最后一种办法可以很快速地求出中缀表达式对应的前缀表达式和后缀表达式,就是添括号去括号法。

  比如有表达式: (3+5*2)-2*3

  先对每一个小部分添加括号: ((3+(5*2))-(2*3))

  然后将每个操作符放到括号后面:((3(52)*)+(23)*)-

  然后去括号:352*+23*-

  便得到了后缀表达式,前缀表达式类似(只需把操作符放到括号前面即可)。

时间: 2024-10-28 14:19:07

中缀表达式求值问题的相关文章

中缀表达式求值 C++ Stack

给一个包含小数的中缀表达式 求出它的值 首先转换为后缀表达式然后利用stack求出值 转换规则: 如果字符为'('  push else if 字符为 ')' 出栈运算符直到遇到'(' else if 字符为'+','-','*','/' { if 栈为空或者上一个运算符的优先级小于当前运算符 push else { 运算符优先级小于等于栈顶运算符的优先级,出栈 然后!将当前运算符入栈! } } 代码 #include<iostream> #include<cstdio> #inc

中缀表达式求值总结

中缀表达式的题目困扰了我两三年,都没去写过..这两天看到2005年提高组的T3要用到所以心血来潮写了一下. 表达式求值借助基本结构应该不用说了是栈,不管手写还是STL都没有太大关系.而中缀表达式最难控制的地方是优先级,算上+-*/^()一共有四个优先级[+-,*/,, ^()](后面会提到一个三级的字符“负号”,这是预留空位). 下面对一个例子进行分析:2*3+4*6/3+17-4^2 那么处理的时候怎么控制优先级呢? 搜到‘+’之前,栈的情况是这样的: 操作数栈:2  3 操作符栈:* 然后搜

刁肥宅详解中缀表达式求值问题:C++实现顺序/链栈解决

1. 表达式的种类 如何将表达式翻译成能够正确求值的指令序列,是语言处理程序要解决的基本问题,作为栈的应用事例,下面介绍表达式的求值过程. 任何一个表达式都是由操作数(亦称运算对象).操作符(亦称运算符)和分界符组成的.通常,算术表达式有3种表示: ①中缀(infix)表示:<操作数><操作符><操作数>,如A+B. ②前缀(prefix)表示: <操作符><操作数><操作数>,如+AB. ③后缀(postfix)表示: <操作

中缀表达式求值

中缀表达式用于计算一个表达式,比如计算器 就是这样实现的 这儿是用栈的数据结构来实现的.首先输入一个字符串,表示一个表达式,然后用一个栈存储数字,另外一个栈存储符号 如果当前运算符优先级比栈顶元素优先级高,则入栈,若当前运算符优先级小于等于栈顶运算符优先级,则从数字栈中弹出两个元素,从符号栈中弹出一个符号,计算结果入栈(数字栈): 代码如下: #include<iostream> using namespace std; #include<cstring> #include<

【数据结构】栈的应用——中缀表达式求值(c++)

头文件: #pragma once #include <iostream> #include <assert.h> #include <string> using namespace std; template<class Type> class SeqStack { public: SeqStack(size_t sz = INIT_SZ); ~SeqStack(); public: bool empty()const; bool full()const;

中缀表达式求值模板

#include <bits/stdc++.h>using namespace std; /*判断符号间的优先关系函数*1表示>,0表示=,-1表示<*c1栈内的算符,c2栈外的算符*/int Judge(char c1,char c2){ int a1,a2; if('+'==c1||'-'==c1) a1 = 3; if('*'==c1||'/'==c1)a1 = 5; if('('==c1) a1 = 1; if(')'==c1) a1 = 7; if('#'==c1) a1

中缀表达式求值问题(栈的应用)

1 #include <iostream> 2 #include<stdlib.h> 3 4 using namespace std; 5 6 #define STACK_INIT_SIZE 100 7 #define STACKINCREASE 10 8 9 //为了简化函数,数据栈和符号栈用了一种结构体(虽然我知道可以用共用体解决问题,嫌烦), 10 //由此导致的后果是接下来所有的运算过程中不允许出现小数,而且由于是利用ASCII表来储存整数, 11 //所以要求运算数以及运

数据结构 栈的应用 --表达式求值

一: 中缀表达式求值  思想: 需要2个栈,运算对象栈OPND,运算符栈OPTR, 1:将栈OPND初始化为空,栈OPTR初始化为表达式的定界符# 2:扫描表达式,直到遇到结束符# 2.1:当前字符是运算对象,入栈OPND 2.2:当前字符是运算符且优先级比栈OPTR的栈顶运算符优先级高,入栈OPTR,处理下一个字符 2.3:当前字符是运算符且优先级比栈OPTR的栈顶运算符优先级低,则从栈OPND出栈2个运算对象,从栈OPTR出栈一个运算符进行运算,并将运算结果入栈OPND,处理当前字符 2.4

栈的应用——表达式求值

表达式求值是程序设计语言编译中的一个基本问题,它的实现就是对“栈”的典型应用.本文针对表达式求值使用的是最简单直观的算法“算符优先法”. 本文给出两种方式来实现表达式求值,方式一直接利用中缀表达式求值,需要用到两个栈,操作数栈和操作符栈.首先置操作数栈为空栈, 操作符栈仅有“#”一个元素.依次读入表达式中的每个字符,若是操作数则进操作数栈,若是操作符则和操作符栈的栈顶运算符比较优先权作相应操作,直至整个表达式求值完毕.方式二首先把中缀表达式转换为后缀表达式并存储起来,然后利用读出的后缀表达式完成