栈的应用-计算表达式
问题:给你一个表达式(当然是字符串),求这个表达式的值。
在javascript中有一个简单的方法可以轻松实现,就是eval,代码如下:
var expr = '-(-1)*(2+(7*5))'; var ret = eval(expr); alert(ret);
要是我们自己写一个这样的函数,那就得用到“栈”了。所谓栈,就是一种特殊的线性表,它遵循“后入先出”的原则,每次只能从栈顶一个一个的取出。这里用JavaScript语言模拟栈,代码如下:
// 模拟一个栈 function Stack(){ var stack = {}, top = -1; return { // 栈是否为空 isEmpty : function(){ return (top == -1 ? true : false); }, // 栈(顶)当前值 curr : function(){ return (top >= 0 ? stack[top] : null); }, // 入栈操作 push : function(o){ ++top; stack[top] = o; }, // 出栈,会返回出栈的值 pop : function(){ if(top < 0){ return null; } var temp = stack[top]; delete stack[top]; --top; return temp; } }; }
下面运用栈来进行表达式的运算操作,首先是要搞清楚思路:
- 取两个栈,一个用来存表达式中的数字,另一个存符号(+ 、- 、* 、/);
- 从表达式开头按字符扫描;若是数字,就入数栈;若是符号,则要分情况处理:
- 若当前符号优先级大于符号栈栈项的优先级,当前符号入符号栈;
- 否则,一直进行如下中间运算操作:
- 依次从数字栈中弹出两个数,分别记为num1, num2;
- 从符号栈中弹出一个符号,记为#
- 进行简单的四则运算:ret = num2 # num1;
- 并将得到的结果ret再入数字栈;
- 完成中间运算操作后,并将当前符号入符号栈;
- 表达式扫描完成后,进行上述中间操作,直到符号栈为空;
上面的思路仅仅是考虑最简单的四则运算,并没有考虑符号为括号时的处理。还有一些细节问题,下面用代码来具体实现(JavaScript语言):
function calc(expr){ // 数字栈、符号栈、表达式索引、表达式的长度、拼接数字的中间变量 var nums = new Stack(), opers = new Stack(), index = 0, max = expr.length, num_temp = ''; // 是否为符号(运算符或括号) var isOper = function(ch){ if(ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(' || ch == ')'){ return true; } return false; }; // 运算优先级 var prec = function(oper){ if(oper == '+' || oper == '-'){ return 1; }else if(oper == '*' || oper == '/'){ return 2; } }; // 两个数的简单基本四则运算 var getResult = function(num1, num2, oper){ switch(oper){ case '+': return num2 + num1; break; case '-': return num2 - num1; break; case '*': return num2 * num1; break; case '/': return num2 / num1; break; } }; /** * 中间运算 * 依次从数字栈中弹出两个数分别记为num1, num2 * 从符号栈中弹出一个符号 * 然后运算,并将得到的结果再 入 数字栈 */ var middle_calc = function(){ num1 = nums.pop(); // 兼容若表达式中第一个字符为'-',比如'-(-1)*2+(7-5)'; // 但不进行处理也没问题 //num2 = nums.pop(); num2 = nums.pop() || 0; oper = opers.pop(); var ret = getResult(num1, num2, oper); nums.push(ret); //console.log(num2); }; // 扫描表达式,进行入栈操作 while(true){ // 当前扫描字符 var ch = expr[index]; // 当前扫描字符为数字 if(!isNaN(ch)){ // 数字进行拼接(以形成多位数) num_temp += ch; // 当前扫描字符为符号 }else if(isOper(ch)){ // 当前符号的前一个数字 入 数字栈, // 并清空拼接数字的中间变量以便拼接下一个数字 if(num_temp !== ''){ nums.push(num_temp * 1); num_temp = ''; } // 符号栈为空 或 扫描的当前符号为'(' 或 当符号栈中的栈顶为'(', // 则扫描的当前符号 入 符号栈 if(opers.isEmpty() || ch == '(' || (opers.curr() == '(')){ opers.push(ch); // 符号栈非空且扫描的当前符号为 运算符(+、-、*、/)时 }else if(ch !== '(' && ch !== ')'){ // 扫描的当前符号优先级 大于 符号栈中的栈顶的优先级 // 则扫描的当前符号 入 符号栈 if( prec(ch) > prec(opers.curr()) ){ opers.push(ch); }else{ // 扫描的当前符号优先级若一直 不大于 符号栈中的栈顶的优先级 // 则一直进行中间运算 while(prec(ch) - prec(opers.curr()) <= 0){ middle_calc(); } // 中间运算完成后,扫描的当前符号 入 符号栈 opers.push(ch); } // 扫描的当前符号为')'时 }else if(ch == ')'){ // 若符号栈中的栈顶不为'(',就一直进行中间运算 while(opers.curr() !== '('){ middle_calc(); } // 中间运算完成后,'(' 出栈 oper = opers.pop(); } }
// 最后一个数字入数字栈
if(num_temp !== ''){
nums.push(num_temp * 1);
num_temp = '';
}
// 表达式索引加1,以扫描表达式中下一个字符 ++index; // 表达式索引到了表达式最末,则完成扫描,退出循环 if(index > max){ break; } } // 扫描完成后的最后运算 while(!opers.isEmpty()){ middle_calc(); } // 数字栈中的最后一个就是表达式的最终运算结果 return nums.pop(); }
验证结果:
成功!
var expr = '-(-1)*(2+(7*5))'; console.log(calc(expr));
浙公网安备 33010602011771号