修改BUG的时候一不小心BUG越修越多,鉴于维护程序并不是学习数据结构的初衷,我已经果断的弃坑了!!
以下内容再不更新,Github上的代码直接无法正常编译运行。。。。
参考参考就好,学习到栈的作用就好。。。
===========================================================
使用数据结构中的栈以及链表来制作一个能够处理中缀表达式的计算程序。
在该程序中输入的数字可以是任意正数、小数。
负数似乎也可以正常运行,即使我没有特意添加检测负数的代码。。。。。
输入的运算符可以是 + - * / ( )
但请确保输入的表达式合法。
中缀表达式:
形如
9+(3-1)*3+10/2
的数学表达式。特点是运算符在被运算的两个数字中间,也就是我们日常接触的算式。
后缀表达式:
以上面的中缀表达式 9+(3-1)*3+10/2 为例
转换成后缀表达式为 9 3 1-3*+ 10 2/+
特点是运算符紧跟在被运算的两个数之后,这样方便程序的理解与计算。
只要顺序读取数字并根据其后的运算符进行相应的操作即可得到结果。
对于以上的转换如有不懂可参考
程序大致分为3步实现:
1)将输入的字符串拆分为数字与运算符,用一个链表储存它们,方便接下来的计算
2)使用仅储存运算符的栈将该中缀表达式转换为后缀表达式
3)使用仅储存数字的栈来运算后缀表达式得到最终答案
为实现这3步,代码有点繁琐。。。用了多个文件来分别实现具体功能
以下是各个代码及大概作用,由于比较长,全部折叠了。
Stack.h & Stack.c
实现了一个储存运算符的栈(用于进行第2步)和一个储存数字的栈(用于第3步)
//////////////
// Stack.h //
////////////// #ifndef _STACK_H_
#define _STACK_H_ #include<stdlib.h> #define STACK_INIT_SIZE 10
#define STACKINCREMENT 10 //优先级的定义
#define TopLow -1
#define Equal 0
#define TopHigh 1
//输入的是个括号
#define InputIsBrace_1 2
#define InputIsBrace_2 3
//当前栈顶是个括号
#define TopIsBrace 4 typedef char SymType;
typedef float NumType; //第一步
//将中缀表达式转换为后缀表达式
//该栈用于储存符号
struct _Operator
{
SymType *Top,*Bottom;
int stacksize;
};
typedef struct _Operator OPERATOR; //新建栈
int InitStack_O(OPERATOR *S); //销毁栈
int DestroyStack_O(OPERATOR *S); //检测栈是否为空
//返回1表示为空
int StackEmpty_O(OPERATOR S); //获取栈顶部元素
SymType GetTop_O(OPERATOR S); //与栈顶的优先级进行比较
int TopIsPriority_O(OPERATOR S, SymType Input,SymType Top); //Push操作,入栈,压栈
int Push_O(OPERATOR *S, SymType e); //Pop操作,出栈
SymType Pop_O(OPERATOR *S); //以上为符号栈
//=====================================================================
//以下为数字栈 //第二步
//计算后缀表达式的值
//用于储存数据
struct _Number
{
NumType *Top, *Bottom;
int stacksize;
};
typedef struct _Number NUMBER; //新建栈
int InitStack_N(NUMBER *S); //销毁栈
int DestroyStack_N(NUMBER *S); //获取栈顶部元素
NumType GetTop_N(NUMBER S); //Push操作,入栈,压栈
int Push_N(NUMBER *S, NumType e); //Pop操作,出栈
NumType Pop_N(NUMBER *S); //接受运算符
//取栈顶的两个数字
//进行相应运算
void DoTheMath(NUMBER*,char); #endif
Stack.h
//////////////
// Stack.c //
///////////// #include"Stack.h" int InitStack_O(OPERATOR *S)
{
//创建出设定长度的顺序表,地址赋给bottom指针
S->Bottom = (SymType*)malloc(STACK_INIT_SIZE*sizeof(SymType));
if (!S->Bottom)
{
return ;
}
S->stacksize = STACK_INIT_SIZE;
S->Top = S->Bottom;
return ;
} int DestroyStack_O(OPERATOR *S)
{
S->Top = NULL;
free(S->Bottom);
S->Bottom = NULL;
return ;
} int StackEmpty_O(OPERATOR S)
{
if (S.Bottom == S.Top)
{
return ;
}
else
{
return ;
}
} SymType GetTop_O(OPERATOR S)
{
if (S.Bottom != S.Top)
{
//由于top指向的是最顶上元素的下一个位置
//所以取出最顶上元素的时候
//要把top减去1
return *(S.Top - );
}
return ;
} //与栈顶的优先级进行比较
int TopIsPriority_O(OPERATOR S, SymType Input,SymType top)
{
if (Input == '(')
{
return InputIsBrace_1;
}
if (Input == ')')
{
return InputIsBrace_2;
}
if (top == '(')
{
return TopIsBrace;
}
//-+优先级为1
//*/优先级为2
int p_top, p_input;
if (top == '-' || top == '+')
{
p_top = ;
}
if (top == '*' || top == '/')
{
p_top = ;
}
if (Input == '-' || Input == '+')
{
p_input = ;
}
if (Input == '*' || Input == '/')
{
p_input = ;
} if (p_input > p_top)
{
return TopLow;
}
if (p_input == p_top)
{
return Equal;
}
if (p_input < p_top)
{
return TopHigh;
}
return ;
} int Push_O(OPERATOR *S, SymType e)
{
//当超出当前栈的容量时进行扩容
//这里应该只有等于的情况
//而不会大于
if ((S->Top - S->Bottom) >= S->stacksize)
{
//realloc函数将开辟指定大小的储存空间
//并将原来的数据全部移到这个新的储存空间
S->Bottom = (SymType*)realloc(S->Bottom, (S->stacksize + STACKINCREMENT)*sizeof(SymType));
if (!S->Bottom)
{
return ;
}
//由于重新开辟了空间
//需要重新根据bottom指针的位置指定top
S->Top = S->Bottom + S->stacksize;
//最后增加当前栈容量
S->stacksize += STACKINCREMENT;
}
//再把入栈的数据存放好
*(S->Top++) = e;
return ;
} SymType Pop_O(OPERATOR *S)
{
if (S->Bottom == S->Top)
{
return ;
}
//top指针先减1再取值
return *(--S->Top);
} //以上为符号栈的函数
//========================================================================
//以下为数字栈的函数 int InitStack_N(NUMBER *S)
{
//创建出设定长度的顺序表,地址赋给bottom指针
S->Bottom = (NumType*)malloc(STACK_INIT_SIZE*sizeof(NumType));
if (!S->Bottom)
{
return ;
}
S->stacksize = STACK_INIT_SIZE;
S->Top = S->Bottom;
return ;
} int DestroyStack_N(NUMBER *S)
{
S->Top = NULL;
free(S->Bottom);
S->Bottom = NULL;
return ;
} NumType GetTop_N(NUMBER S)
{
if (S.Bottom != S.Top)
{
//由于top指向的是最顶上元素的下一个位置
//所以取出最顶上元素的时候
//要把top减去1
return *(S.Top - );
}
return ;
} int Push_N(NUMBER *S, NumType e)
{
//当超出当前栈的容量时进行扩容
//这里应该只有等于的情况
//而不会大于
if ((S->Top - S->Bottom) >= S->stacksize)
{
//realloc函数将开辟指定大小的储存空间
//并将原来的数据全部移到这个新的储存空间
S->Bottom = (NumType*)realloc(S->Bottom, (S->stacksize + STACKINCREMENT)*sizeof(NumType));
if (!S->Bottom)
{
return ;
}
//由于重新开辟了空间
//需要重新根据bottom指针的位置指定top
S->Top = S->Bottom + S->stacksize;
//最后增加当前栈容量
S->stacksize += STACKINCREMENT;
}
//再把入栈的数据存放好
*(S->Top++) = e;
return ;
} NumType Pop_N(NUMBER * S)
{
if (S->Bottom == S->Top)
{
return ;
}
//top指针先减1再取值
return *(--S->Top);
} void DoTheMath(NUMBER *S,char sym)
{
//从栈顶顺序读入两个数字
//进行运算
//并将结果压回栈内
float num1, num2;
num2 = Pop_N(S);
num1 = Pop_N(S);
switch (sym)
{
case '+':
Push_N(S, num1 + num2);
break;
case '-':
Push_N(S, num1 - num2);
break;
case '*':
Push_N(S, num1*num2);
break;
case '/':
Push_N(S, num1 / num2);
break;
default:
break;
}
}
Stack.c
StringToNumAndSym.h & StringToNumAndSym.c
这个文件的功能是。。。。
将输入的一大串字符串拆分成一个数字或一个运算符,并将他们按顺序储存到一个链表里。
用于进行第1步。
////////////////////////////////
//StringToNumAndSym.h//
/////////////////////////////// #ifndef _STRINGTONUMANDSYM_H_
#define _STRINGTONUMANDSYM_H_ #include<stdlib.h> //用于储存输入的数字或者符号
//IsNum=1 将读取数字
//IsNum=0 将读取字符
struct data_node
{
int IsNum;
float Num;
char Sym;
struct data_node *next;
};
typedef struct data_node EQUATION; //该函数接受输入的字符串
//输出一串处理好的等式的指针
//用于表示数字或者运算符号加减乘除等等
void StringToEquation(char *,EQUATION**); void DestroyEquation(EQUATION*); #endif
StringToNumAndSym.h
/////////////////////////////////
// StringToNumAndSym.c //
/////////////////////////////// #include"StringToNumAndSym.h" void StringToEquation(char *input, EQUATION **ptr_source)
{
EQUATION *ptr = NULL;
int lop;
//记录正数部分的temp
int Ptmp;
//记录小数部分的temp
float Dtmp;
//PNum记录正数的位数
//NNum记录小数的个数
float Dmulti;
int Pmulti; for (lop = ; input[lop] != ; lop++)
{
//接下来有个数字
//除错,确保输入合法
if (input[lop] >= '' && input[lop] <= ''
|| input[lop] == '+' || input[lop] == '-' || input[lop] == '/' || input[lop] == '*'
|| input[lop] == '(' || input[lop] == ')' || input[lop] == '.')
{
if (input[lop] >= '' && input[lop] <= '')
{
Ptmp = ;
Dtmp = ;
Pmulti = ;
Dmulti = ; //计算整数部分
for (; input[lop] >= '' && input[lop] <= ''; lop++)
{
Ptmp = Ptmp * + (input[lop] - '');
} //如果有小数
//计算小数部分
if (input[lop] == '.')
{
Dmulti = (float)0.1;
//计算出小数部分
for (lop += ; input[lop] >= '' && input[lop] <= ''; lop++)
{
Dtmp += Dmulti*(float)(input[lop] - '');
Dmulti *= (float)0.1;
}
}
//得到数字了
//创建节点保存
if (ptr == NULL)
{
ptr = (EQUATION*)malloc(sizeof(EQUATION));
//创建第一个节点之后
//由于传入的是指针的指针
//需要更改ptr_source的值
//保存头指针
*ptr_source = ptr;
}
else
{
ptr->next = (EQUATION*)malloc(sizeof(EQUATION));
ptr = ptr->next;
}
ptr->next = NULL;
ptr->IsNum = ;
ptr->Num = (float)Ptmp + Dtmp;
ptr->Sym = ''; lop--;
}
else
{
//第一个节点
if (ptr == NULL)
{
ptr = (EQUATION*)malloc(sizeof(EQUATION));
*ptr_source = ptr;
}
//之后的节点
else
{
ptr->next = (EQUATION*)malloc(sizeof(EQUATION));
ptr = ptr->next;
}
ptr->next = NULL;
ptr->IsNum = ;
ptr->Num = ;
ptr->Sym = input[lop];
}
}
}
} /*
//反转整数
int Invert(int input)
{
int output = 0;
while (input > 0)
{
output = output * 10 + input % 10;
input /= 10;
}
return output;
}
*/ void DestroyEquation(EQUATION *N)
{
EQUATION *temp = NULL;
if (N != NULL)
{
temp = N;
N = N->next;
free(temp);
temp = NULL; DestroyEquation(N);
}
}
StringToNumAndSym.c
main.c
主函数在此,实现了判断运算符运算顺序的逻辑
/////////////
// main.c //
///////////// #include<stdio.h> #include"Stack.h"
#include"StringToNumAndSym.h" int main()
{
int lop = ; //新建用于储存输入数据的字符串
char string[] = { };
//新建用于储存处理后数据的字符串
EQUATION *Eqinput; //以下4行创建并初始化两个栈
//分别用于存放运算符和数字
OPERATOR Stack_Ope;
NUMBER Stack_Num; InitStack_O(&Stack_Ope);
InitStack_N(&Stack_Num); //输入合法的中缀表达式
//不要太长
printf("请输入合法的中缀表达式(不带空格):\n");
scanf("%s", string); //将输入的字符串转化为能够处理的数据
StringToEquation(string, &Eqinput); //将中缀表达式转换为后缀表达式
//同时包含对数据的运算 //如果输入的数据还没有处理完
while (Eqinput != NULL)
{
//看看当前对象是不是数字
if (Eqinput->IsNum == )
{
//是数字就直接丢到处理数字的栈里去
Push_N(&Stack_Num, Eqinput->Num);
}
else
{
if (StackEmpty_O(Stack_Ope))
{
Push_O(&Stack_Ope, Eqinput->Sym);
} //是运算符的话
//将他与上一个运算符,也就是栈顶的那个运算符进行比较
//然后根据两者的优先级大小来做出相应操作
else
{
//优先级判断
switch (TopIsPriority_O(Stack_Ope,Eqinput->Sym,GetTop_O(Stack_Ope)))
{
//如果栈顶是括号
//不进行优先级的比较
//直接入栈
case TopIsBrace:
Push_O(&Stack_Ope, Eqinput->Sym);
break; //输入了左括号
case InputIsBrace_1:
Push_O(&Stack_Ope, '(');
break; //输入了右括号
//一直出栈
//直到遇到左括号
case InputIsBrace_2:
while((GetTop_O(Stack_Ope) != '('))
{
//printf("%c ", Pop_O(&Stack_Ope));
DoTheMath(&Stack_Num, Pop_O(&Stack_Ope));
}
Pop_O(&Stack_Ope);
break; //当前运算符优先级比栈顶的低或者相等
case TopHigh:
case Equal:
while (!StackEmpty_O(Stack_Ope) )
{
if (GetTop_O(Stack_Ope) == '(')
{
break;
}
//printf("%c ", Pop_O(&Stack_Ope));
DoTheMath(&Stack_Num, Pop_O(&Stack_Ope));
}
Push_O(&Stack_Ope, Eqinput->Sym);
break; //当前运算符优先级比栈顶的高
case TopLow:
Push_O(&Stack_Ope, Eqinput->Sym);
break;
default:
break;
}
}
}
//读入下一个数据
Eqinput = Eqinput->next;
} //如果栈里还有残留的运算符
//全部出栈
while (!StackEmpty_O(Stack_Ope))
{
//printf("%c ", Pop_O(&Stack_Ope));
DoTheMath(&Stack_Num, Pop_O(&Stack_Ope));
} printf("\n"); //////////////
//运算完毕//
///////////// printf("%s = %.3f\n", string, GetTop_N(Stack_Num)); //销毁输入的式子
DestroyEquation(Eqinput);
Eqinput = NULL; DestroyStack_N(&Stack_Num);
DestroyStack_O(&Stack_Ope); return ;
}
main.c
运行效果: