这里若是对于形如字符串“((6+((7+8)-9)*9+8/2)-3)/2”的运算表达式进行运算。接触过此类的同窗知道这种存在着运算符优先级的表达式,不能直接从左到右进行运算,咱们使用OperandStack操做数栈和OperatorStack操做符栈,对操做符进行比较,肯定优先级后,取出操做数进行运算。算法
算法思想以下:ide
1.首先肯定操做的符的优先级,*、/大于+、-,(大于*、/,*、/或者+、-一块儿时,排在前面的运算符优先,)的优先级最小,且)与(相遇时抵消lua
2.从左到右遍历字符串,每次遍历一个字符,设置数字临时存储变量OperandTempspa
①当遇到操做符时, 若是OperandTemp有数值,把数字压入到OperandStack中code
②循环OperatorStack,直到OperatorStack没值为止,而后比较这个操做符和OperatorStack顶部的运算符进行比较,若是此操做符运算优先级高,将此运算符压入栈,退出循环;若是此操做符运算优先级低,则将OperatorStack栈顶的运算符取出ope1,从OperandStack中取出顶部的两个数值a1(先出栈)和a2,注意首先出栈的作第二个操做数,则进行blog
a2 ope1 a1;求得结果压人OperandStack中;若是此操做符是)遇到(时,将OperatorStack栈中的(消除字符串
③循环到最后会剩余2中状况,即OperatorStack中剩1个运算符,剩余两个运算符,且最后一个运算符优先级高,则读取最后一个数字和OperandStack顶的数字进行操做运算,求得结果,再运算。string
以字符串“((6+((7+8)-9)*9+8/2)-3)/2”为例,算法的运算过程是,先将((压入OperatorStack变为【((】,将6压入OperandStack【6】,到+,因为(不参与运算,则将+压入it
OperatorStack【((+】,下一个字符(的优先级大于OperatorStack顶的+,则将(压入OperatorStack【((+(】,下一个(压入OperatorStack【((+((】,7压入OperandStack【6,7】,下一个+同理压入io
OperatorStack【((+((+】,8压入OperandStack【6,7,8】,下一个)优先级小于OperatorStack顶部的+,则取出OperandStack顶部的8和7和OperatorStack顶部的+,运算得15压入OperandStack,继续比较)和OperatorStack顶的(,优先级相同,同时消去(,此时OperatorStack为【((+(】,OperandStack位【6,15】,下一个字符-小于(,入栈【((+(—】,9入栈OperandStack为【6,15,9】;下一个),取出-和15,9进行运算得6,入栈OperandStack【6,6】;)消去栈顶(OperatorStack为【((+】;下一个*同理,OperandStack【6,6】,OperatorStack为【((+*】;下一个9入栈OperandStack【6,6,9】;下一个+,优先级小于*,则6*9=54入栈,OperandStack【6,54】,OperatorStack为【((+】;下一个+,优先级小,则取出栈顶+6+54=60入栈OperandStack【60】,压入此运算符+OperatorStack为【((+】;下一个8入栈OperandStack【60,8】;下一个/,优先级大入栈,2入栈,则OperatorStack为【((+/】,OperandStack【60,8,2】;下一个)优先级小,则
OperatorStack为【((+】,OperandStack【60,4】=》OperatorStack为【(】,OperandStack【64】;下一个-入栈,3入栈OperatorStack为【(-】,OperandStack【64,3】;下一个),则64-3=61入栈,OperatorStack为【空】,OperandStack【61】;下一个/入栈,2入栈,OperatorStack为【/】,OperandStack【61,2】;此为剩下一操做符的状况,最后运算获得结果:61/2=30.5
实现代码以下:
1 static char[] Operators = new char[] { '+', '-', '*', '/', '(', ')' }; 2 static void Main(string[] args) 3 { 4 float a = EvaluateExpression("10+(80*3+(6+7))*2"); 5 Console.WriteLine(a); 6 Console.ReadKey(); 7 8 } 9 /// <summary> 10 /// 初始化运算符优先级 11 /// </summary> 12 /// <param name="a"></param> 13 /// <param name="b"></param> 14 /// <returns></returns> 15 static char InitPriorities(char a, char b) 16 { 17 int aIndex = -1; 18 int bIndex = -1; 19 for (int i = 0; i < Operators.Length; i++) 20 { 21 if (Operators[i] == a) 22 aIndex = i; 23 if (Operators[i] == b) 24 bIndex = i; 25 26 } 27 char[,] Priorities = new char[6, 6] {{'>','>','<','<','<','>'}, 28 {'>','>','<','<','<','>'}, 29 {'>','>','>','>','<','>'}, 30 {'>','>','>','>','<','>'}, 31 {'<','<','<','<','<','='}, 32 {'?','?','?','?','?','?'}}; 33 return Priorities[aIndex, bIndex]; 34 } 35 static float Calculate(float Operand1, float Operand2, char Operator) 36 { 37 float Ret = 0; 38 if (Operator == '+') 39 { 40 Ret = Operand1 + Operand2; 41 } 42 else if (Operator == '-') 43 { 44 Ret = Operand1 - Operand2; 45 } 46 else if (Operator == '*') 47 { 48 Ret = Operand1 * Operand2; 49 } 50 else if (Operator == '/') 51 { 52 Ret = Operand1 / Operand2; 53 } 54 55 return Ret; 56 } 57 static float EvaluateExpression(string str) 58 { 59 Stack<float> OperandStack = new Stack<float>(); // 操做数栈, 60 Stack<char> OperatorStack = new Stack<char>(); // 操做符栈 61 float OperandTemp = 0; 62 63 char LastOperator = '0'; // 记录最后遇到的操做符 64 65 for (int i = 0, size = str.Length; i < size; ++i) 66 { 67 char ch = str[i]; 68 69 if ('0' <= ch && ch <= '9') 70 { // 读取一个操做数 71 OperandTemp = OperandTemp * 10 + ch - '0'; 72 } 73 else if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || 74 ch == '(' || ch == ')') 75 { 76 // 有2种状况 是没有操做数须要入栈保存的。 77 // 1 当前操做符是 “(”。(的左边的操做符已经负责操做数入栈了。 78 // 2 上一次遇到的操做符是“)”。)自己会负责操做数入栈,)后面紧跟的操做符不须要再负责操做数入栈。 79 if (ch != '(' && LastOperator != ')') 80 { 81 // 遇到一个操做符后,意味着以前读取的操做数已经结束。保存操做数。 82 OperandStack.Push(OperandTemp); 83 // 清空,为读取下一个操做符作准备。 84 OperandTemp = 0; 85 } 86 87 // 当前遇到的操做符做为操做符2,将和以前遇到的操做符(做为操做符1)进行优先级比较 88 char Opt2 = ch; 89 90 for (; OperatorStack.Count > 0; ) 91 { 92 // 比较当前遇到的操做符和上一次遇到的操做符(顶部的操做符)的优先级 93 char Opt1 = OperatorStack.Peek(); 94 char CompareRet = InitPriorities(Opt1, Opt2); 95 if (CompareRet == '>') 96 { // 若是操做符1 大于 操做符2 那么,操做符1应该先计算 97 98 // 取出以前保存的操做数2 99 float Operand2 = OperandStack.Pop(); 100 101 // 取出以前保存的操做数1 102 float Operand1 = OperandStack.Pop(); 103 104 // 取出以前保存的操做符。当前计算这个操做符,计算完成后,消除该操做符,就不必保存了。 105 OperatorStack.Pop(); 106 107 // 二元操做符计算。并把计算结果保存。 108 float Ret = Calculate(Operand1, Operand2, Opt1); 109 OperandStack.Push(Ret); 110 } 111 else if (CompareRet == '<') 112 { // 若是操做符1 小于 操做符2,说明 操做符1 和 操做符2 当前都不能进行计算, 113 // 退出循环,记录操做符。 114 break; 115 } 116 else if (CompareRet == '=') 117 { 118 // 操做符相等的状况,只有操做符2是“)”,操做数1是“(”的状况, 119 // 弹出原先保存的操做符“(”,意味着“(”,“)”已经互相消掉,括号内容已经计算完毕 120 OperatorStack.Pop(); 121 break; 122 } 123 124 } // end for 125 126 // 保存当前遇到操做符,当前操做符还缺乏右操做数,要读完右操做数才能计算。 127 if (Opt2 != ')') 128 { 129 OperatorStack.Push(Opt2); 130 } 131 132 LastOperator = Opt2; 133 } 134 135 } // end for 136 137 138 /* 139 上面的 for 会一面遍历表达式一面计算,若是能够计算的话。 140 当遍历完成后,并不表明整个表达式计算完成了。而会有2种状况: 141 1.剩余1个运算符。 142 2.剩余2个运算符,且运算符1 小于 运算符2。这种状况,在上面的遍历过程当中是不能进行计算的,因此才会被遗留下来。 143 到这里,已经不须要进行优先级比较了。状况1和状况2,都是循环取出最后读入的操做符进行运算。 144 */ 145 if (LastOperator != ')') 146 { 147 OperandStack.Push(OperandTemp); 148 } 149 for (; OperatorStack.Count > 0; ) 150 { 151 // 取出以前保存的操做数2 152 float Operand2 = OperandStack.Pop(); 153 154 // 取出以前保存的操做数1 155 float Operand1 = OperandStack.Pop(); 156 157 // 取出末端一个操做符 158 char Opt = OperatorStack.Pop(); 159 160 // 二元操做符计算。 161 float Ret = Calculate(Operand1, Operand2, Opt); 162 OperandStack.Push(Ret); 163 } 164 165 return OperandStack.Peek(); 166 }