栈的应用--计算字符串表达式

计算机的本质工作就是做数学运算,那计算机可以读入字符串"1+2+3+4+5+6+7"并计算值吗?

答案是肯定的。

这里我基本实现了个位数的加减乘除,当然这个算法最简单的解决方式是采用二叉树(后面会实现~),这里作出了栈的实现方式。


首先引入两个概念:

中缀表达式和后缀表达式

1,在生活中我们通常书写1+1的时候都会写成1+1,废话~,这就是中缀表达式,更符合人们的思维习惯与想法。

2,所谓后缀表达式就是将运算符写在操作数的后面,这样更符合计算机的思维。

举例:

 

5 + 3            =>  5 3 +

1 + 2 * 3        =>   1 23 * +

9 + ( 3 – 1 ) * 5 =>  9 3 1 – 5 * +


为什么会这样的呢?我来用我的大白话作一下解释,比如 9 + ( 3 – 1 ) * 5 =>   9 3 1 – 5 * +

后缀表达式在实现计算的时候,遇到符号就向左寻找操作数,比如上面的9 3 1后面就有 - 号,此时计算机就会将3作为左操作数,1作为右操作数,也就是计算3-1的值,继续前进,遇到5 pass,遇到*号,就会将刚刚计算好的3-1的值作为左操作数,5作为右操作数,即为(3-1)*5,继续前进,遇到+号,就会计算9+(3-1)*5的值。就是这样~


那么计算机是如何将中缀表达式转换为后缀表达式和如何将后缀表达式的值求出来的呢?


中缀转后缀:

遍历中缀表达式中的数字和符号

对于数字:直接输出

对于符号:

  ->左括号:进栈

  ->符号:与栈顶符号进行优先级比较

        栈顶符号的优先级低:符号进栈

        栈顶符号的优先级高:将栈顶符号弹出并输出,之后进栈

  ->右括号:将栈中的所有符号弹出并输出


计算后缀的值:

遍历后缀表达式中的数字和符号

对于数字:进栈

对于符号:

  ->从栈中弹出右操作数

  ->从栈中弹出左操作数

  ->根据符号进行运算

  ->将运算结果压入栈中

 遍历结束:栈中的唯一数字为计算结果




好了,我们来用代码实现以下,跟上一篇一样,这里同样需要用到栈的代码,同样这里就不再贴了,请参阅: 栈的实现与操作(C语言实现)


// 栈的应用计算表达式.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <memory.h>
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include "LinkStack.h"
#include <malloc.h>
//判断是否为数字
int isNumber(char c)
{
    return ('0' <= c) && (c <= '9');
}
//判断是否为操作符
int isOperator(char c)
{
    return (c == '+') || (c == '-') || (c == '*') || (c == '/');
}
//判断是否为:(
int isLeft(char c)
{
    return (c == '(');
}
//判断是否为:)
int isRight(char c)
{
    return (c == ')');
}
//比较优先级
int priority(char c)
{
    int ret = 0;
    
    if( (c == '+') || (c == '-') )
    {
        ret = 1;
    }
    
    if( (c == '*') || (c == '/') )
    {
        ret = 2;
    }
    
    return ret;
}
//输出
void output(char c)
{
    if( c != '\0' )
    {
        printf("%c", c);
    }
}

//中缀转后缀
void transform(const char* exp)
{
    LinkStack* stack = LinkStack_Create();
    int i = 0;	
    
    while( exp[i] != '\0' )
    { 
		//判断是否为数字
        if( isNumber(exp[i]) )
        {
            output(exp[i]);
			
			
        }
		//判断是否为操作符
        else if( isOperator(exp[i]) )
        {
			//遍历栈内符号,如果当前操作符的优先级小于栈顶的操作符,则弹出栈顶,并输出
            while( priority(exp[i]) <= priority((char)(int)LinkStack_Top(stack)) )
            {
                output((char)(int)LinkStack_Pop(stack));
				
				
            }
            //将当前操作符压入栈
            LinkStack_Push(stack, (void*)(int)exp[i]);
        } 
		//判断是否为左括号:( ,如果是,则压入栈
        else if( isLeft(exp[i]) )
        {
            LinkStack_Push(stack, (void*)(int)exp[i]);
        } 
		//判断是否为右括号: )
        else if( isRight(exp[i]) )
        {
            char c = '\0';//没看出有什么用,哈哈
            
			//遍历栈内元素,如果不是左括号:(,就一直输出
            while( !isLeft((char)(int)LinkStack_Top(stack)) )
            {
                output((char)(int)LinkStack_Pop(stack));			
            }
            //弹出左括号:(
            LinkStack_Pop(stack);
        }
        else
        {
            printf("Invalid expression!");
            break;
        }
        
        i++;
    }
	//弹出栈内所有元素
    while( (LinkStack_Size(stack) > 0) && (exp[i] == '\0') )
    {
        output((char)(int)LinkStack_Pop(stack));		
		i++;
	
    }
    //销毁栈
    LinkStack_Destroy(stack);
	
}


int value(char c)
{
    return (c - '0');
}

//计算左右操作数的值
int express(int left, int right, char op)
{
    int ret = 0;
    
    switch(op)
    {
        case '+':
            ret = left + right;
            break;
        case '-':
            ret = left - right;
            break;
        case '*':
            ret = left * right;
            break;
        case '/':
            ret = left / right;
            break;
        default:
            break;
    }
    
    return ret;
}

//计算整个表达式的值
int compute(const char* exp)
{
    LinkStack* stack = LinkStack_Create();
    int ret = 0;
    int i = 0;
	
    
    while( exp[i] != '\0' )
    {
        if( isNumber(exp[i]) )
        {
            LinkStack_Push(stack, (void*)value(exp[i]));
			
        }
        else if( isOperator(exp[i]) )
        {
            int right = (int)LinkStack_Pop(stack);
            int left = (int)LinkStack_Pop(stack);
            int result = express(left, right, exp[i]);
            
            LinkStack_Push(stack, (void*)result);
        }
        else
        {
            printf("Invalid expression!");
            break;
        }
        
        i++;
    }
    
    if( (LinkStack_Size(stack) == 1) && (exp[i] == '\0') )
    {
        ret = (int)LinkStack_Pop(stack);
    } 
    else 
    {
        printf("Invalid expression!");
    }
    
    LinkStack_Destroy(stack);
    
    return ret;
}

int _tmain(int argc, _TCHAR* argv[])
{
	printf("8 * 2 + 1 - ( 5 - 1 ) / 2 + 2 - 1中缀转后缀后结果为:");
	transform("8*2+1-(5-1)/2+2-1");    
    printf("\n");		
	printf("8 * 2 + 1 - ( 5 - 1 ) / 2 + 2 - 1 = %d\n", compute("82*1+51-2/-2+1-"));

	system("pause");
	return 0;
}

运行结果:

8 * 2 + 1 - ( 5 - 1 ) / 2 + 2 - 1中缀转后缀后结果为:82*1+51-2/-2+1-
8 * 2 + 1 - ( 5 - 1 ) / 2 + 2 - 1 = 16
请按任意键继续. . .





如有错误,望不吝指出。





因各个项目中需要使用根据字符串计算数值,这里写出一个算法,专门计算字符串。配有大量常用公式。只有一个人方法,直接调用即可。 类名:CustomMath 函数名:Calculations(string value) 说明:求解算式表达式字符串的值 表达式中包含的符号或函数: truncate, ceiling,floor,round,log10, sign,sinh,sqrt, asin,atan,cosh, tanh, sin,cos,tan ,abs,acos, exp,log,max,min,pow,mod,+,-,*,/,',',(,) 函数说明:(不区分大小写) truncate(num) 计算指定数的整数部分 truncate(1.23)=1 ceiling (num) 返回大于或等于指定的双精度浮点数的最小整数值 ceiling(1.23)=2 floor(num) 返回小于或等于指定双精度浮点数的最大整数 floor(1.23)=1 round(num) 将双精度浮点值舍入为最接近的整数值 round(1.23)=1 round(num,num1) 将小数值按指定的小数位数舍入 round(1.23,1)=1.2 log10(num) 返回指定数字以 10 为底的对数 log10(10)=1 sign(num) 返回表示数字符号的值 sign(1.23)=1 sinh(num) 返回指定角度的双曲正弦值 sinh(1.23)=1.5644 sqrt(num) 返回指定数字的平方根 sqrt(9)=3 sqrt(num,num1) 返回指定数字的num1根 sqrt(27,3)=3 asin(num) 返回正弦值为指定数字的角度 asin(0.5)=PI/6 atan(num) 返回正切值为指定数字的角度 atan(1)=45 cosh(num) 返回指定角度的双曲余弦值 cosh(1.23)=1.8567 tanh(num) 返回指定角度的双曲正切值 tanh(1.23)=0.8425 sin(num) 返回指定角度的正弦值 sin(PI/6)=0.5 cos(num) 返回指定角度的余弦值 sin(PI/3)=0.5 tan(num) 返回指定角度的余切值 sin(PI/4)=1 abs(num) 返回数字的绝对值 abs(-12)=12 acos(num) 返回余弦值为指定数字的角度 acos(0.5)=PI/3 exp(num) 返回 e 的指定次幂 exp(1)=2.718 log(num) 返回指定数字的自然对数(底为 e) log(e)=1 log(num,num1) 返回指定数字在使用指定底时的对数 log(e,e)=1 max(num,um1) 返回最大值 max(1,2)=2 min(num,num1) 返回最小值 min(1,2)=1 pow(num,num1) 返回指定数字的指定次幂 pow(2,2)=4 mod(num,num1) 返回余数 mod(3,2)=1 常量: PI 值:3.14159265358979323846 E 值:2.7182818284590452354 YEAR 值:当前年份 MONTH 值:当前月份 DAY 值: 当前日 HOUR 值:当前时 MINUTE 值:当前分 SECOND 值:当前秒 RANDOM 值:一个随机数(0-1 之间) 实例 系统计算:1+2*3/4-0.5=2 函数计算:1+2*3/4-0.5=2 调用方式:CustomMath.Calculations("1+2*3/4-0.5") 系统计算:(1+2)*3/4-0.5=1.75 函数计算:(1+2)*3/4-0.5=1.75 调用方式:CustomMath.Calculations("(1+2)*3/4-0.5") 系统计算:(sin(pi)+sqrt(3+5*7+(2+8/4*5+2)))/6=1.20185042515466 公式计算:(sin(pi)+sqrt(3+5*7+(2+8/4*5+2)))/6=1.20185042515466 调用方式:CustomMath.Calculations("(sin(pi)+sqrt(3+5*7+(2+8/4*5+2)))/6") 系统计算:sin(pow(3,2)/4)+3.5-9*sqrt(81)=-76.7219268031121 函数计算:sin(pow(3,2)/4)+3.5-9*sqrt(81)=-76.7219268031121 调用方式:CustomMath.Calculations("sin(pow(3,2)/4)+3.5-9*sqrt(81)")
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值