修改BUG的时候一不小心BUG越修越多,鉴于维护程序并不是学习数据结构的初衷,我已经果断的弃坑了!!

以下内容再不更新,Github上的代码直接无法正常编译运行。。。。

参考参考就好,学习到栈的作用就好。。。

===========================================================

使用数据结构中的栈以及链表来制作一个能够处理中缀表达式的计算程序。

在该程序中输入的数字可以是任意正数、小数。

负数似乎也可以正常运行,即使我没有特意添加检测负数的代码。。。。。

输入的运算符可以是 + - * / ( )

但请确保输入的表达式合法。

中缀表达式:

形如

9+(3-1)*3+10/2

的数学表达式。特点是运算符在被运算的两个数字中间,也就是我们日常接触的算式。

后缀表达式:

以上面的中缀表达式 9+(3-1)*3+10/2 为例

转换成后缀表达式为 9 3 1-3*+ 10 2/+

特点是运算符紧跟在被运算的两个数之后,这样方便程序的理解与计算。

只要顺序读取数字并根据其后的运算符进行相应的操作即可得到结果。

对于以上的转换如有不懂可参考

将中缀表达式转化为后缀表达式

GitHub地址

程序大致分为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

运行效果:

数据结构应用实例#栈&单链表#简易计算器的更多相关文章

  1. 数据结构之 线性表---单链表的操作B(先逆序+再删除重复元素)

    数据结构上机测试2-2:单链表操作B Time Limit: 1000MS Memory limit: 65536K 题目描述 按照数据输入的相反顺序(逆位序)建立一个单链表,并将单链表中重复的元素删 ...

  2. 数据结构之 线性表---单链表操作A (删除链表中的指定元素)

    数据结构上机测试2-1:单链表操作A Time Limit: 1000MS Memory limit: 4096K 题目描述 输入n个整数,先按照数据输入的顺序建立一个带头结点的单链表,再输入一个数据 ...

  3. Java数据结构-线性表之单链表LinkedList

    线性表的链式存储结构,也称之为链式表,链表:链表的存储单元能够连续也能够不连续. 链表中的节点包括数据域和指针域.数据域为存储数据元素信息的域,指针域为存储直接后继位置(一般称为指针)的域. 注意一个 ...

  4. 2、java数据结构和算法:单链表: 反转,逆序打印, 合并二个有序链表,获取倒数第n个节点, 链表的有序插入

    什么也不说, 直接上代码: 功能点有: 1, 获取尾结点 2, 添加(添加节点到链表的最后面) 3, 添加(根据节点的no(排名)的大小, 有序添加) 4, 单向链表的 遍历 5, 链表的长度 6, ...

  5. 《数据结构》2.3单链表(single linked list)

    //单链表节点的定义 typedef struct node { datatype data; struct node *next; }LNode,*LinkList; //LNode是节点类型,Li ...

  6. C:数据结构与算法之单链表

    单链表相对于顺序表比较难理解,但是比较实用,单链表的插入,删除不需要移动数据元素,只需要一个指针来寻找所需要的元素,还有一个大优点就是不浪费空间,当你想要增加一个结点可以申请(malloc())一个结 ...

  7. python数据结构与算法之单链表

    表的抽象数据类型 ADT list:                      #一个表的抽象数据类型 List(self)               #表的构造操作,创建一个空表 is_empty ...

  8. 数据结构C语言版--单链表的基本功能实现

    /* * 构造一个链式存储的线性表(当输入9999时,结束构造过程),然后输出该线性表 * 并统计该线性链表的长度 . *注:new和delete是C++的运算符 malloc和free是C++/C的 ...

  9. 【c++版数据结构】之循环单链表的实现(带头结点以及尾节点)

    所实现的循环单链表的结构例如以下图所看到的: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill ...

随机推荐

  1. mcp2515 芯片驱动总线错误BUG的解决方法

    http://blog.renren.com/share/221002615/11483613167 来自张涛的日志 现象:CAN总线在线上设备热插拔或长时间运行后出现总线异常情况,有时不能发送和接收 ...

  2. 深入解析Microsoft Sql server 2008

    http://blog.csdn.net/downmoon/article/details/5256548

  3. C++ - 模板函数须要类型转换时使用友元(friend)模板函数

    模板函数须要类型转换时使用友元(friend)模板函数 本文地址: http://blog.csdn.net/caroline_wendy/article/details/24357301 非模板函数 ...

  4. docker国内镜像拉取和镜像加速registry-mirrors配置修改

    docker国内镜像拉取和镜像加速registry-mirrors配置修改 学习了:http://blog.csdn.net/u014231523/article/details/61197945 站 ...

  5. Python网络爬虫(一):初步认识网络爬虫

    不管你是因为什么原因想做一个网络爬虫,首先做的第一件事情就是要了解它. 在了解网络爬虫之前一定要牢记下面4个要点,这是做网络爬虫的基础: 1.抓取 py的urllib不一定去用.可是要学.假设你还没用 ...

  6. 使用Jsoup解析和操作HTML

    jsoup 简单介绍 jsoup 是一款 Java 的HTML 解析器,可直接解析某个URL地址.HTML文本内容.它提供了一套很省力的API,可通过DOM.CSS以及类似于jQuery的操作方法来取 ...

  7. Android Message和obtainMessage的差别

    前几天须要实现一个以太网功能就看了以太网的源代码部分,看见了源代码部分在消息处理时,发现有一些不同的地方:   平时我在处理消息时:   1.首先创建Handler对象:   private Hand ...

  8. Object-C---&gt;Swift之(十一)属性观察者

    属性观察者机制能让程序在属性被赋值时获得运行代码的机会,用来监视属性的除初始化之外的属性值变化,当属性值发生改变时能够对此作出响应 详细包含两个特殊的回调方法: willSet(newValue):被 ...

  9. android 程序猿跳槽须要注意哪些?

    我是一个工作3年多的android开发,因为公司和个人发展原因.打算跳槽! 这次跳槽又给我好好的上了一课!所以我自己反思总结了一下.然后整理出一下几点 程序猿打算跳槽的时候须要注意的几点. 一 先想好 ...

  10. LeetCode 541. Reverse String II (反转字符串 II)

    Given a string and an integer k, you need to reverse the first k characters for every 2k characters ...