表达式求解有有多种解决办法,也有很多方法是先把表达式转换为后缀式在求解的。我这里是使用2个栈来直接求解的,一个栈用来保存运算符,一个用来保存数字。
基本的思路也就是,程序根据输入的字符,来分步解析,并计算。用2个数组来表示运算符和运算符的优先级,通过运算符的优先级来决定是否进行运算还是入栈。
static char operas[]={'#','+','-','*','/'};
static char priority[]={'0','1','1','2','2'};
运算符优先级就用这2个字符数组表示,其中#号作为一个标记符用来表示最小优先级的运算符,在运算符栈初始化的时候就先存一个#运算符入栈,并且将#运算符作为输入的字符结尾表示结束。
比如输入一个字符串:12+56/7*65# 遇到数字,就用一个length变量来保存数字长度,当遇到下一个运算符的时候。使用字符串截取函数,截取前面的字符并转换为数字,并将这数字入栈,然后字符串指针位置在+号这里,程序判断+号和运算符栈顶的运算符优先级,因为运算符栈初始化是有一个最低级运算符#,所以+号优先级大于#号,这里就将+号也入运算符栈。指针继续指向数字,截取数字56后入运算数栈,这里/号运算符优先级大于刚入栈的+号。所以/号也继续入预算符栈。数字7入运算数栈,后面的*号和运算数栈顶的/号同优先级,所以这里程序先不把*号入栈,进行出栈计算函数操作。由于运算数栈栈顶和栈顶下一个数据,就是/号运算符的2个运算数,所以将运算数栈出栈2次,运算符栈出栈一次,得到56/7,进行计算,将得到的结果8进行运算数入栈操作,然后在执行*号和运算符栈顶进行比较,*号优先级大于+号,入运算符栈,并且读取数字65,入运算数栈,最后读取的#号优先级小于所有运算符,所以进行出栈运算操作,同样是运算数栈出栈2次,而运算符栈出栈一次,得到8*65.计算后入运算数栈。栈计算函数进行递归判断,#号优先级继续小于第一次入栈的+号,在执行出栈运算,知道运算符栈也只有最先初始化的#号运算符结束。
上面就是这个表达式基本运算的步骤,这里实现的也只是简单的+-/*。并没有加入()等界定符。用栈来进行表达式求解的基本思路也就是:当前运算符优先级比运算符栈顶的优先级大的时候,进行运算符入栈操作,等于或小于的时候,进行出栈计算操作,直到遇到#号运算符。下面有一个c写的例子,没有找到c语言中如何实现可以存储任意数据类型的栈表示方法,就暂且用void*指针表示了,所以代码中有很多强制类型转换的代码。
栈的定义
#ifndef STACK_S
#define STACK_S
#define Type double
typedef Type ElemType;
typedef struct _stack{
ElemType data;
int size;
struct _stack* top;
}stack;
//定义一个栈数据结构,size栈大小,data存储数据。top表示栈顶指针。
extern stack* newstack();
//分配栈内存
extern stack* StackCreate();
//创建一个栈
extern void push(stack* s,ElemType data);
extern ElemType peek(stack* s);
extern ElemType pop(stack* s);
//栈的放入,查看,和出栈操作
#endif//STACK_S
demo.c
/*
** Copyright (C) QPSOFT.COM All rights reserved.
static char operas[]={'#','+','-','*','/'};
static char priority[]={'0','1','1','2','2'};
定义2个字符数组,用来表示运算符优先级,2个字符数组想对应。可以使用二维数组,不过2个一维比较直观。
static int len=sizeof(operas)/sizeof(char);
返回运算符数组长度
int Inopera(char ch,char* chs);
判断ch字符是不是运算符
int Comopera(char o1,char o2,int len);
比较运算符和运算符栈的栈顶运算符优先级
double calc(double oa,double ob,char opera);
计算,oa,ob分别为运算数栈的栈顶和下一个数据,opera为运算符栈顶数据表示的运算符
char* substring(char* ch,int pos,int length);
字符串截取
void stack_opera(char* pch,stack* o_stack,stack* n_stack);
栈处理函数,用来判断表达式入栈后的结果
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "stack.h"
#define TRUE 1
//预处理,
static char operas[]={'#','+','-','*','/'};
static char priority[]={'0','1','1','2','2'};
static int len=sizeof(operas)/sizeof(char);
/*函数声明*/
int Inopera(char ch,char* operas);
int Comopera(char o1,char o2,int len);
double calc(double oa,double ob,char opera);
char* substring(char* ch,int pos,int length);
void stack_opera(char* pch,stack* o_stack,stack* n_stack);
int main()
{
stack* n_stack=StackCreate();
stack* o_stack=StackCreate();
//定义运算符o_stack和运算数n_stack栈
push(o_stack,operas[0]);//运算符栈初始化一个最低优先级运算符#
char* text="5-2.5#";//要进行计算的运算数,这里定义类型为long
char *pch=text;//定义一个指针指向运算数
int pos=0,length=0;/*pos指向计算字符中的位置,length来保存其中每端数字的长度*/
while(TRUE)
{
length++;
if(*pch=='.'||(*pch<'9' && *pch>='0'))
{
pch++;
continue;/*判断 *pch是否是一个数字,用length来保存连续数字的长度*/
}
else if(Inopera(*pch,operas))
{
push(n_stack,atof(substring(text,pos,length-1)));
pos+=length;
length=0;
/*Inopera函数来判断是否遇到运算符,并将前面的数字截取,
//然后入栈,atof方法转换字符为float数字*/
stack_opera(pch,o_stack,n_stack);/*遇到运算符,stack_opera函数来判断执行那种操作*/
}
else
{
printf("%s\n","expression is error!");
break;/*如果遇到的字符不是数字或不是指定的运算符,程序将退出*/
}
if((*pch)=='#'){break;}
++pch;/*pch指针,每循环一次递增*/
}
printf("%f\n",pop(n_stack));
/*显示运算数栈最后得到的结果*/
return 0;
}
/*计算函数,参数分别表示2个计算数,opera表示运算符*/
double calc(double oa,double ob,char opera)
{
/*根据opera运算符来进行运算*/
switch(opera)
{
case '+':
return (oa+ob);
case '-':
return (oa-ob);
case '*':
return (oa*ob);
case '/':
return (oa/ob);
}
return 0;
}
/*运算符判断函数,ch是要判断的运算符,operas是指定的运算符数组*/
int Inopera(char ch,char* operas)
{
while(*(operas)!='\0')
{
if(ch==(*operas++))
{
return 1;
}
}/*如果ch和operas字符数组中的数匹配,则返回1,否则返回0*/
return 0;
}
/*运算符优先级判断*/
int Comopera(char o1,char o2,int len)
{
int i,ol1,ol2=-1;/*声明几个局部变量,i是因为非c99规范,不支持for循环中声明变量,所以定义在外部保持兼容*/
/*ol1,ol2用来表示运算符o1,o2在运算符数组中的位置,用来表示优先级*/
for(i=0;i<len;i++)
{
if(o1==operas[i]){ol1=priority[i];}
if(o2==operas[i]){ol2=priority[i];}
}
return ol1-ol2;
/*operas和priority数组是大小相对应的,所以可以根据运算符
//o1,o2在operas中的位置来定位在priority中的位置,并获得优先级大小*/
}
/*栈运算处理函数*/
void stack_opera(char* pch,stack* o_stack,stack* n_stack)
{
/* *pch表示计算数中的当前运算符,o_stack和n_stack表示运算符,运算数栈*/
if(Comopera(*pch,(char)peek(o_stack),len)>0)
{
push(o_stack,*pch);/*如果*pch表示的运算符优先级小于运算符栈顶的,就讲pch入栈*/
}
else /*当*pch表示运算符优先级小于或等于运算符栈顶时,进行出栈计算操作*/
{
double ob=pop(n_stack);
double oa=pop(n_stack); /*将运算数栈出栈2次,获取运算数,并以反顺序赋值。*/
push(n_stack,calc(oa,ob,(char)pop(o_stack))); /*将result入栈。*/
if((char)peek(o_stack)=='#' && *pch=='#'){return;} /*当运算符栈顶为#,并pch指向计算数末尾时返回*/
stack_opera(pch,o_stack,n_stack); /*递归处理栈中存留数据*/
}
}
/*字符截取函数*/
char* substring(char* ch,int pos,int length)
{
char* pch=ch;
char* subch=calloc(sizeof(char),length+1);
int i;
pch=pch+pos; /*定义局部字符指针,初始化pch位置。并分配一个length+1长度,保存char类型的数组内存。*/
for(i=0;i<length;i++)
{
subch[i]=*(pch++);
}
subch[length]='\0'; /*依次赋值填充subch字符数组空间,末尾加上字符串结束标志'\0'*/
return subch;
}