1. 数据结构与算法 -- 开端
Niklaus Wirth提出过一个著名的公式:
程序 = 数据结构 + 算法
----所谓程序,即用于控制计算机的工作流程,完成指定的逻辑功能,最终实现某种任务的指令。而数据结构,以书本的画来说,就是现实世界的数据及其间关系的反映。至于算法,则是程序的逻辑抽象,是解决某类客观问题的流程。
----我们深知,人们能够利用计算机解决实际的问题。为了贴合计算机的基本技术需求,我们需要对目标建立模型,然后确定恰当的数据结构表示该模型,并且以此为基础,设计合适的计算方法。最终,满足我们解决问题的最终需求。
----综上所述,我们引入第一个实际问题:
1.1 程序栈(Stack)
----栈是一种具有特殊运算要求的线性表,其仅能在表尾进行插入和删除操作。在这种条件下,我们把表尾称为栈顶 Top,表头称为栈底 Base。总结起来,它的特性如下:
- 栈限定只能于表的端点进行插入与删除。
- 栈具有后进先出的特性,即LIFO(Last in first out)
- 插入元素到栈顶(表尾)的操作,即入栈。
- 从栈顶(表尾)删除最后一个元素的操作,即出栈。
- 入栈出栈均可以随时进行。
1.2 表达式求值 -- 问题分析
----在设计方案之前,我们要先对表达式的组成做一个分析。它分为三个主要部分:第一个部分是操作数(Operand),通常由常数和变量组成。第二个部分是运算符(Operator),主要划分为算术运算符、关系运算符和逻辑运算符。第三个部分是界限符(Delimiter),核心为左右括弧和表达式结束符。我们知道,任何一个算术表达式都包括操作数、算术运算符和界限符构成,因此,解决问题的核心就在于:如何表达这三个部分,并找出它们的优先级次序。
----确定了主要方向之后,我们就要着手开始构造算法。根据各类资料,我们选取由“运算符优先级确定运算顺序的表达式求值算法”,即算符优先算法。该算法可以使用两个工作栈,一个设为 Operator,用于寄存运算符。一个设为 Operand,用于寄存操作数和运算结果。考虑到我是以“上机实验”作为主要设计目标的,所以基础操作不引入 Stack,一切以完整程序设计为准。
----梳理一下任务,我们任务目标是以下几点:
- 构造函数 In(C):判断 C 是否为运算符。
- 构造函数 Precede (number1,number2):判断运算符 number1、number2 优先级。
- 构造函数 Operate (a,theta,b):对 a 和 b 进行二元运算 theta。
----任务基础标准是算术基本准则:
- 我们必须遵守“先乘除、后加减”的原则。
- 我们要选择从左至右进行计算。
- 我们要先计算括号内的表达式,再计算括号外的表达式。
1.3 表达式求值 -- 进一步剖析
----我们知道,算符之前存在优先级关系。但是,单独考虑任何一种算符,都会造成程序设计上的混乱。基于这个问题,我依照资料提示,绘制了一张表格,以基础的四则运算作为基础,表述了其先后关系。其中,“>”为高优先级,“<”为低优先级,“=”为相同优先级。
----对于本问题的解决计划也就随之产生了:
第一步、 我们要设定两个栈:
-
OPND -- 代表操作数和运算结果
-
OPTR -- 代表运算符 第二步、 我们要初始化 OPTR 和 OPND ,将表达式起始符“#”压入 OPTR 。紧接着,我们扫描表达式,读入第一个字符 ch ,如果表达式没有扫描完毕至“#”或者 OPTR 的栈顶元素不为“#”时,则循环如下操作:
-
如若 ch 不是运算符,则压入 OPND ,读入下一个字符 ch 。
-
如若 ch 是运算符,则根据 OPTR 的栈顶元素和 ch 的优先级比较结果,做出不同的处理。
第三步,对于具体的处理方式,我们给出如下解释:
- 如果是“小于”,则 ch 压入 OPTR, 读出下一个字符 ch
- 如果是“大于”,则弹出 OPTR 栈顶的运算符,从 OPND 栈弹出两个数,进行对应的运算,结果再压入 OPND 。
- 如果是“等于”,则 OPTR的栈顶元素是 “(” 而且 ch 是“)”,此时弹出 OPTR 栈顶的“(”,相当于括号匹配完成,读下一个字符 ch 。
----最终 OPND 栈顶元素就是表达式求值的结果,返回此元素即可。我们用表格再次对以上的计划进行一次分析。
1.4 表达式求值 -- 基础四则运算的代码解析
----根据各类资料以及我自己的思考,我决定先创建一个栈的基本构架,然后逐步实现函数 In 、Percede 、Operate,最终按照示例之中的运算表述过程完善程序编写。(注意:我谨以学习目的,参考了大量有关问题的代码资料,因此本代码虽然是我自己独自键入的,但是仅作参考)
代码如下:
第一步,我尝试使用了结构体,构造了基本的栈(Sqstack)
//引入库文件
#include <iostream>
#include "WriteStack.h"
#include<cmath>
#include <string>
#include <algorithm>
#include<stdlib.h>
#include<vector
//以下为需要定义的常量
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OVERFLOW -2
#define MAXSIZE 10000000
//设置一些特殊变量
using namespace std;
typedef char SElemType;
typedef int NElemType;
typedef int Status;
第二步,我尝试初始化运算符栈和操作数栈,并且创造最基本的功能。最开始的部分,我希望通过使用结构体,构造出运算符栈和操作数栈,并且赋予它们入栈、出栈、栈满提示、取栈顶元素等几项能力,方便我们进行下一步的计算。(其间,我把运算符栈称之为 S型,把操作数栈称之为 N型)
//我们开始定义结构体类型
typedef struct {
SElemType *base;
SElemType *top;
int stacksize;
}SqStack_optr; //运算符栈类型
typedef struct {
NElemType *base;
NElemType *top;
int stacksize;
}SqStack_opnd;//操作数栈类型
//我们给一个初始化的函数构造(运算符栈初始化)
Status InitStackS ( SqStack_optr &S )
{
S.base = new SElemType[MAXSIZE];
//新建连续存储空间:类型确定为SElemType, 其大小为 MAXSIZE(10000) 然后赋值给栈底指针
if (!S.base)
return OVERFLOW;
//分配失败返回 -2
S.top = S.base;
//把base栈底指针的地址也赋值给栈顶指针
S.stacksize = MAXSIZE;
//赋值栈的容量
return OK;
}
//我们给一个初始化的函数构造(操作数栈初始化)
Status InitStackN ( SqStack_opnd &S )
{
S.base = new NElemType[MAXSIZE];
//新建连续存储空间:类型确定为SElemType, 其大小为 MAXSIZE(10000) 然后赋值给栈底指针
if (!S.base)
return OVERFLOW;
//分配失败返回 -2
S.top = S.base;
//把base栈底指针的地址也赋值给栈顶指针
S.stacksize = MAXSIZE; //赋值栈的容量
return OK;
}
//运算符插入操作
Status PushS( SqStack_optr &S, SElemType e)
{
if( S.top - S.base == S.stacksize ) //栈满
return ERROR;
*S.top ++ = e;
return OK;
}
//操作数插入操作
Status PushN( SqStack_opnd &S, NElemType e)
{
if( S.top - S.base == S.stacksize ) //栈满
return ERROR;
*S.top ++ = e;
return OK;
}
//运算符栈出栈操作
Status PopS( SqStack_optr &S, SElemType &e)
{
if( S.top == S.base ) // 栈变空
return ERROR;
e= *--S.top;
return OK;
}
//操作数栈出栈操作
Status PopN( SqStack_opnd &S, NElemType &e)
{
if( S.top == S.base ) // 栈变空
return ERROR;
e= *--S.top;
return OK;
}
//运算符栈取栈顶元素
Status GetTopS( SqStack_optr S)
{
SElemType e;
if(S.top==S.base)
return ERROR;
e=*(S.top-1);
return e;
}
//操作数栈取栈顶元素
Status GetTopN( SqStack_opnd S)
{
NElemType e;
if(S.top==S.base)
return ERROR;
e=*(S.top-1);
return e;
}
第三步,我们要开始准备判断目标字符,如果该字符属于运算符,就返回 TRUE。如果该字符不属于运算符,就返回 FALSE。在此处,我们加入了第一个扩展内容,即取余运算。通过细心观察不难发现,取余运算和乘除运算是没有区别的,他们的优先级没有先后之分,两个符号也不会紧挨在一起。 由此,我们可以直接插入取余运算的功能项,完成之后的代码。
//判断 字符c 是否为运算符,如果是运算符,那么返回TRUE。如果不是运算符,那么返回FALSE
Status In(SElemType c)//应在前面有定义 typedef char SElemType
{
switch(c)
{ //逐个判断其是否为运算符
case '+':return TRUE;
case '-':return TRUE;
case '*':return TRUE;
case '/':return TRUE;
case '%':return TRUE;
case '(':return TRUE;
case ')':return TRUE;
case '#':return TRUE;
default: return FALSE;
}
}
//判断运算符的优先级,如果 运算符1 大于 运算符2, OPND出栈,运算符1出栈,完成运算后压栈OPND
//如果 运算符1 小于 运算符2, 运算符2压栈
//如果 运算符1 等于 运算符2,脱括号
SElemType Precede(SElemType t1,SElemType t2)
{ //细心观察其优先级顺序
SElemType f;
switch (t2)
{
case '+': if(t1=='('||t1=='#')
f='<';
else
f='>';
break;
case '-': if(t1=='('||t1=='#')
f='<';
else
f='>';
break;
case '*': if(t1=='('||t1=='#'||t1=='+'||t1=='-')
f='<';
else
f='>';
break;
case '/': if(t1=='('||t1=='#'||t1=='+'||t1=='-')
f='<';
else
f='>';
break;
case '%': if(t1=='('||t1=='#'||t1=='+'||t1=='-')
f='<';
else
f='>';
break;
case '(': if(t1=='=')
f='=';
else
f='<';
break;
case ')': if(t1=='('||t1=='#')
f='=';
else
f='>';
break;
case '#': if(t1=='('||t1=='#')
f='=';
else
f='>';
break;
}
return f;
}
//
NElemType Operate(NElemType a,SElemType theta,NElemType b)
{
NElemType c;
switch(theta)
{
case '+':
c=a+b;
break;
case '-':
c=a-b;
break;
case '*':
c=a*b;
break;
case '/':
c=a/b;
break;
case '%':
c=a%b;
break;
}
return c;
}
第四步,也就是核心部分,我们要编写个位数限制的运算程序。在函数之中,我们要定义出 OPND 和 OPTR,按照上文的设计方案,对二者进行初始化,并且对输入的合法运算表达式进行压入(Push)、计算(Operate)、出栈(Pop)。
//主要运算用的程序(个位数)
int EvaluateExpressionOne(){
SqStack_opnd OPND;
SqStack_optr OPTR;
int a,b;
char ch,x,theta;
InitStackS(OPTR);
PushS(OPTR,'#');
InitStackN(OPND);
cin>>ch;
while(ch!='#'||GetTopS(OPTR) != '#')
{
if(!In(ch))
{
PushN(OPND,ch-'0');
cin>>ch;
} //如果ch不是运算符,则让其进栈
else
switch (Precede(GetTopS(OPTR),ch)) //我们来开始比较优先权
{
case '<': //当前字符ch压入OPTR栈,读入下一字符ch
PushS(OPTR,ch);
cin>>ch;
break;
case '>': //弹出OPTR栈顶的运算符运算,并将运算结果入栈
PopS(OPTR,theta);
PopN(OPND,b);
PopN(OPND,a);
PushN(OPND,Operate(a,theta,b));
break;
case '=': //脱括号并开始接受下一个字符的信息
PopS(OPTR,x);
cin>>ch;
break;
}
} return GetTopN(OPND);
}
----到这里,表达式求值问题的基础部分就完成了。现在,如果我在主程序之中调用上述函数,就能够完成最基本的“加、减、乘、除、取余”几项计算。(仅限于个位数,且运算式必须合法)
2. 有趣的拓展内容 -- 进阶
----在编写程序时,我注意到了几个很有意思的点。首先,假若我们需要十位数以上的运算单位,那该怎么解决呢?其次,假若我们需要存在小数的运算过程,那该如何实现呢?紧接着,如果我们需要使用逻辑运算关系,那该如何改造代码呢?最后,如果我们需要添加负数运算,那该如何编写新函数呢?综合以上问题,我们开始逐一解决。
2.1 表达式求值 -- 拓展表达式求值的代码解析
----本质上,如果需要十位数以上的运算单位,其原理与个位数差别不大,我们可以引入一个判断数,用于对比上次读取的是否是数字的标志位置,初始设为 0,遇到数字改为 1,遇到字符改为 0。
代码如下:
//主要运算用的程序(多位数)
int EvaluateExpressionPlural(){
SqStack_opnd OPND;
SqStack_optr OPTR;
int a,b,t,e;
char ch,x,theta;
InitStackS(OPTR);
PushS(OPTR,'#');
InitStackN(OPND);
cin>>ch;
int isnum=0; //对位数进行判断
while(ch!='#'||GetTopS(OPTR) != '#')
{
if(!In(ch))
{
if(isnum==1)
{
PopN(OPND,e);
t=ch-'0';
PushN(OPND,e*10+t);
isnum=1;
cin>>ch;
}
else{
PushN(OPND,ch-'0');
isnum=1;
cin>>ch;
}
} //如果ch不是运算符,则让其进栈
else
{
isnum=0;
switch (Precede(GetTopS(OPTR),ch)) //我们来开始比较优先权
{
case '<': //当前字符ch压入OPTR栈,读入下一字符ch
PushS(OPTR,ch);
cin>>ch;
break;
case '>': //弹出OPTR栈顶的运算符运算,并将运算结果入栈
PopS(OPTR,theta);
PopN(OPND,b);
PopN(OPND,a);
PushN(OPND,Operate(a,theta,b));
break;
case '=': //脱括号并开始接受下一个字符的信息
PopS(OPTR,x);
cin>>ch;
break;
}
} //else
} return GetTopN(OPND);
}
以上代码有几个变量需要注意:
- InitStackS(OPTR) 这个函数的用处是初始化一个运算符栈,并将“#”压入栈底以便最终判断表达式是否完成。
- InitStackN(OPND) 这个函数的用处是初始化一个操作数栈,存放数字以及中间的运算结果。
- t = ch-'0' 执行这个操作的原因是 ch 为字符类型的数字,所以将其转化为真正的数时,需要减掉 0 的ASCII编码差值。
----同理,在处理小数运算时,我们也可以很容易就明确要做的目标。为了更有意思一些,我通过查询资料找到了一种利用二维数组的代码比较方法,重新定义了 Precede() 。由于操作数和运算符都只能用字符型来输入,我们得用 ASCII 编码实现区分。那就先从写出 “stack.h” 开始(懒癌发作)。
具体代码如下:
#ifndef _WIRTE_STACK_H_
#define _WIRTE_STACK_H_
#include<iostream>
#include<stdlib.h>
using namespace std;
#define MAXSIZE 10000000
template<class type>
class Write_stack
{
int top;
type* my_s;
int maxsize;
public:
Write_stack():top(-1),maxsize(MAXSIZE)
{
my_s=new type[maxsize];
if(my_s==NULL)
{
cerr<<"分配失败"<<endl;
exit(1);
}
}
Write_stack(int size):top(-1),maxsize(size)
{
my_s=new type[maxsize];
if(my_s==NULL)
{
cerr<<"分配失败"<<endl;
exit(1);
}
}
~Write_stack()
{
delete[] my_s;
}
//检查栈是不是空的
bool Empty();
//压入
void Push(type tp);
//返回栈顶
type Top();
//出栈
void Pop();
//栈的大小
int Size();
};
template<class type>
bool Write_stack<type>::Empty()
{
if(top==-1){
return true;
}
else
return false;
}
template<class type>
type Write_stack<type>::Top()
{
if(top != -1)
{
return my_s[top];
}
else
{
cout<<"这个栈她变空了!!\n";
exit(1);
}
}
template<class type>
void Write_stack<type>::Push(type tp)
{
if(top+1<maxsize)
{
my_s[++top]=tp;
}
else
{
cout<<"栈满\n";
exit(1);
}
}
template<class type>
void Write_stack<type>::Pop()
{
if(top>=0)
{
top--;
}
else
{
cout<<"栈空\n";
exit(1);
}
}
template<class type>
int Write_stack<type>::Size()
{
return top+1;
}
#endif
----我在编辑这部分代码时,出现了 redefinition 故障。这个问题在于,include 指令把”.h"文件的内容在”.h"文件之前展开,如果没有条件编译语句,程序会重复引用和循环递归 include 指令。解决方案非常简单,只要在上下代码部分加入:
#ifndef _Aim_H_
#define _Aim_H_ //两者一致
class Aim { ....... };
#endif
----问题就会迎刃而解。(亲测有效,救命)
//这是为小数计算准备的新方法
Write_stack<char>OPTR_new;
Write_stack<double>OPND_new;
typedef struct
{//输入表达式时,操作数若长度大于 2 无法正确得到操作数
char data[10];
int length;
}NumberSaving;
bool In_new(char ch)//判断输入的是不是运算符,若是则返回true
{
if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(' || ch == ')'||ch=='#')
{
return true;
}
else return false;
}
char Precede_new(char x, char y)//运算符优先级的判断
{
int Square[2];
char opnd[2] = { x,y };
char data_data[7][7] = {
{'>','>','<','<','<','>','>'},
{'>','>','<','<','<','>','>'},
{'>','>','>','>','<','>','>'},
{'>','>','>','>','<','>','>'},
{'<','<','<','<','<','=',' '},
{'>','>','>','>',' ','>','>'},
{'<','<','<','<','<',' ','='}
};
for (int count = 0; count < 2; count++)
{
switch (opnd[count])
{
case '+':Square[count] = 0;
break;
case '-':Square[count] = 1;
break;
case '*':Square[count] = 2;
break;
case '/':Square[count] = 3;
break;
case '(':Square[count] = 4;
break;
case ')':Square[count] = 5;
break;
case '#':Square[count] = 6;
break;
}
}
return data_data[Square[0]][Square[1]];
}
double Operate_new(double a, char theta, double b)//加、减、乘、除运算
{
double result;
switch (theta)
{
case '+': result=a + b;
break;
case '-':result= a - b;
break;
case '*':result= a * b;
break;
case '/':result= a / b;
break;
}
return result ;
}
double CharForNum(NumberSaving NumSave)//字符数字转换成完整的数字型数字
{
int point=0;//用来判断小数点的位置
double result=0;
for (int i = 0; i < NumSave.length; i++)//先找出小数点的具体位置
{
if (NumSave.data[i] == '.')
break;
++point;
}
for (int i=0,j = 0; i < NumSave.length; i++)//转换数字
{
if (i != point)//小数点不放入计算
{
result += (NumSave.data[i] - '0')*pow(10, point - j - 1);
j++;
}
}
return result;
}
double EvaluateExpressionPoint()
{
bool judge = true;//用来判断是否输入了一个数字。输入完成就让其转化为数字型,存入栈中
NumberSaving NumSave;//用来记录字符,转换为数字
OPTR_new.Push('#');//将表达式起始符压入栈中,用于进行判断
char ch,theta;//记录
int i=0;
double a, b;//记录
cout<<"请输入需要计算的表达式,以'#'结束"<<endl;
cin >> ch;
NumSave.length = 0;
while (ch != '#' || OPTR_new.Top() != '#')// ch=='#'并不能停止循环
{
if (!In_new(ch))
{
NumSave.data[i] = ch;
++i;
++NumSave.length;
judge = true;
cin >> ch;
}
else {
if (judge)//思路:即当输入运算符时,表明运算符的前一个操作数已经输入完毕
//此时可以将结构存储的数字字符转换成double型,并将数字进入OPND栈
{
OPND_new.Push(CharForNum(NumSave));//字符转换数字
NumSave.length = 0; i = 0;//所有回到初始化,用以记录下一个数字
judge = false;//操作数进栈成功后必须将flag取反。
//若进行case '>'时,ch还需再次进入while循环
//进而判断ch与OPTR栈顶操作符,比较优先级
//因为ch还需进入while循环,所以在此之前要把 flag=false,
//若flag=true不再改变,这时数字0进栈,导致运算出错
}
switch (Precede_new(OPTR_new.Top(), ch))
{
case '>': {
theta = OPTR_new.Top();
OPTR_new.Pop(); //取OPND栈顶两个数以及OPTR栈顶运算符进行运算,结果再入栈
b= OPND_new.Top();
OPND_new.Pop();
a= OPND_new.Top();
OPND_new.Pop();
OPND_new.Push(Operate_new(a, theta, b));
break;
}
case '<': {
OPTR_new.Push(ch);
cin >> ch;
break;
}
case '=': {
OPTR_new.Pop();
cin >> ch;
break;
}
}
}
}
return OPND_new.Top();
}
2.2 表达式求值 -- 逻辑运算问题
----上述代码完整实现了小数运算,借此,我们可以开始思考逻辑运算到底如何实现。顾名思义,代码组合需要尝试引入逻辑或、逻辑与、逻辑非的相关运算。虽然运算内容改变了,但是运算规律依旧可以被上文的基础方法纵向剖析。我尝试画出了一张表格,用来描述逻辑运算的先后顺序(如下图所示)。
根据图片之中所示的关系,我组织的代码如下:
string charactor;
char symbol[6] = { '!','&','|','(',')','#' };
char order[6][6] =
{
'>', '>', '>', '<', '>', '>',
'<', '>', '>', '<', '>', '>',
'<', '<', '>', '<', '>', '>',
'<', '<', '<', '<', '=', '>',
'>', '>', '>', '<', '>', '>',
'<', '<', '<', '<', '>', '=',
};
int findsetting(char option)
{
for (int i = 0; i < 6; i++)
{
if (symbol[i] == option)
return i;
}
}
char compare(char a, char b)
{
int x = findsetting(a);
int y = findsetting(b);
return order[x][y];
}
int calculator(int x, int y, char option)
{
if (option == '|')
return x | y;
if (option == '&')
return x&y;
return 0;
}
int EvaluateExpressionLogic()
{
getchar ();
while (getline(cin, charactor))
{
int lens = charactor.size();
charactor += "#";
charactor += '\0';
int t, x, y;
char option;
int i = 0;
Write_stack<int>P;
Write_stack<char>Q;
Q.Push('#'); //末尾符号“#”
while (charactor[i] != '#' || Q.Top() != '#')
{
if (charactor[i] == '1' || charactor[i] == '0')
{
if (charactor[i] == '1')
t = 1;
else
t = 0;
P.Push(t);//遇到数字就往P里放
i++;
}
else if (charactor[i] != ' ')//输入可包含空格,略过空格
{
switch (compare(Q.Top(), charactor[i]))//若是运算符则和Q栈的栈顶运算符比较优先级
{
case '<': // 栈顶元素优先级低,则当前元素入栈
Q.Push(charactor[i]);
i++;
break;
case '=': //当一对括号相遇时表示括号内已运算完成,脱括号并接收下一字符
Q.Pop();
i++;
break;
case '>': // 退栈并将运算结果入栈
if (Q.Top() == '!')
{ // 栈顶元素->!
x = P.Top();
P.Pop();
P.Push(!x);
Q.Pop();
}
else
{ // 栈顶元素非!
x = P.Top();
P.Pop();
y = P.Top();
P.Pop();
option = Q.Top();
Q.Pop(); //计算结果并入栈
P.Push(calculator(x, y, option));
}
break;
}
}
}
int target = P.Top();
cout << target;
}
return 0;
}
----逻辑运算的难度实际上并不如小数运算高。因为其只需要考虑左右计算顺序和括号即可,甚至不存在复杂的计算关系,数字上也不会过于麻烦。所以相较于下文的内容,逻辑运算的扩展可以说是比较友好的。
2.3 表达式求值 -- 负数运算问题
----负数运算需要牵扯的东西太多了。第一点,考虑到负数打乱了符号位置的规律,只通过栈的知识很难完全处理运算过程,因为两种同类型的符号会在放在一起,不能通过单一的判断来完成运算。第二点,负数运算需要用容器(vector),新的数据处理方式也会给整体制造难度。第三点,负数运算需要将中缀表达式换算成后缀表达式,方便计算。这件事可以用二叉树实现,但是我们也能依靠栈和容器的知识来完成。
具体代码如下:
string format(string str_one){
for(int i = 0;i < str_one.length(); i++){
if(str_one[i] == '-')
{
if(i == 0)
{
str_one.insert(0,1,'0');
}else if(str_one[i-1] == '('){
str_one.insert(i,1,'0');
}
}
}
return str_one;
}
int orderone(char c){
if(c == '+' || c == '-'){
return 1;
}else if(c == '*' || c == '/'){
return 2;
}else{
return 0;
}
}
vector<string> horizon(string str_one){
vector<string> visual;
Write_stack<char> Des_one;
for(int i = 0;i < str_one.length(); i++){
string tmp = "";
switch(str_one[i]){
case '+':
case '-':
case '*':
case '/':
if(Des_one.Empty() || Des_one.Top() == '('){
Des_one.Push(str_one[i]);
}else{
while(!Des_one.Empty() && orderone(Des_one.Top()) >= orderone(str_one[i]) ){
tmp += Des_one.Top();
visual.push_back(tmp);
Des_one.Pop();
tmp = "";
}
Des_one.Push(str_one[i]);
}
break;
case '(':
Des_one.Push(str_one[i]);
break;
case ')':
while(Des_one.Top() != '('){
tmp += Des_one.Top();
visual.push_back(tmp);
Des_one.Pop();
tmp = "";
}
Des_one.Pop();
break;
default:
if((str_one[i]>='0' && str_one[i]<='9')){
tmp += str_one[i];
while(i+1<str_one.size() && str_one[i+1]>='0' && str_one[i+1]<='9'||str_one[i+1] == '.')
{
tmp += str_one[i+1];
++i;
}
visual.push_back(tmp);
}
}
}
while(!Des_one.Empty()){
string tmp = "";
tmp += Des_one.Top();
visual.push_back(tmp);
Des_one.Pop();
}
return visual;
}
double result(vector<string> bh){
Write_stack<double> Des_two;
double num,op1,op2;
for(int i = 0;i < bh.size(); i++){
string tmp = bh[i];
if(tmp[0] >= '0'&&tmp[0] <= '9'){
num = atof(tmp.c_str());
Des_two.Push(num);
}
else if(bh[i]=="+")
{
op2=Des_two.Top();
Des_two.Pop();
op1=Des_two.Top();
Des_two.Pop();
Des_two.Push(op1+op2);
}
else if(bh[i]=="-")
{
op2=Des_two.Top();
Des_two.Pop();
op1=Des_two.Top();
Des_two.Pop();
Des_two.Push(op1-op2);
}
else if(bh[i]=="*")
{
op2=Des_two.Top();
Des_two.Pop();
op1=Des_two.Top();
Des_two.Pop();
Des_two.Push(op1*op2);
}
else if(bh[i]=="/")
{
op2=Des_two.Top();
Des_two.Pop();
op1=Des_two.Top();
Des_two.Pop();
Des_two.Push(op1/op2);
}
}
return Des_two.Top();
}
void solve(string str_one){
str_one = format(str_one);
vector<string> bh = horizon(str_one);
double k = result(bh);
if((int)k == k){
cout<<k<<endl;
} else{
printf("%.1f",k);
cout<<endl;
}
}
int EvaluateExpressionLess(){
string str_one;
getchar();
// getchar();
while(getline(cin,str_one)){
solve(str_one);
}
}
3. 栈(Stack)与容器(vector) -- 小结
----本文是我在数据结构与算法课程之中的一些笔记心得。我处在学习阶段,有很多方法不甚知晓,只能通过拙劣的模仿来实现目标。我会在下文贴上整个程序的代码块,就当是一种对于现行资料的学习总结吧。
以下是 Stack 部分:
#ifndef _WIRTE_STACK_H_
#define _WIRTE_STACK_H_
#include<iostream>
#include<stdlib.h>
using namespace std;
#define MAXSIZE 10000000
template<class type>
class Write_stack
{
int top;
type* my_s;
int maxsize;
public:
Write_stack():top(-1),maxsize(MAXSIZE)
{
my_s=new type[maxsize];
if(my_s==NULL)
{
cerr<<"分配失败"<<endl;
exit(1);
}
}
Write_stack(int size):top(-1),maxsize(size)
{
my_s=new type[maxsize];
if(my_s==NULL)
{
cerr<<"分配失败"<<endl;
exit(1);
}
}
~Write_stack()
{
delete[] my_s;
}
//检查栈是不是空的
bool Empty();
//压入
void Push(type tp);
//返回栈顶
type Top();
//出栈
void Pop();
//栈的大小
int Size();
};
template<class type>
bool Write_stack<type>::Empty()
{
if(top==-1){
return true;
}
else
return false;
}
template<class type>
type Write_stack<type>::Top()
{
if(top != -1)
{
return my_s[top];
}
else
{
cout<<"这个栈她变空了!!\n";
exit(1);
}
}
template<class type>
void Write_stack<type>::Push(type tp)
{
if(top+1<maxsize)
{
my_s[++top]=tp;
}
else
{
cout<<"栈满\n";
exit(1);
}
}
template<class type>
void Write_stack<type>::Pop()
{
if(top>=0)
{
top--;
}
else
{
cout<<"栈空\n";
exit(1);
}
}
template<class type>
int Write_stack<type>::Size()
{
return top+1;
}
#endif
然后是求解问题的核心代码:
//引入库文件
#include <iostream>
#include "WriteStack.h"
#include<cmath>
#include <string>
#include <algorithm>
#include<stdlib.h>
#include<vector>
//以下为需要定义的常量
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OVERFLOW -2
#define MAXSIZE 10000000
//设置一些特殊变量
using namespace std;
typedef char SElemType;
typedef int NElemType;
typedef int Status;
//我们开始定义结构体类型
typedef struct {
SElemType *base;
SElemType *top;
int stacksize;
}SqStack_optr; //运算符栈类型
typedef struct {
NElemType *base;
NElemType *top;
int stacksize;
}SqStack_opnd;//操作数栈类型
//我们给一个初始化的函数构造(运算符栈初始化)
Status InitStackS ( SqStack_optr &S )
{
S.base = new SElemType[MAXSIZE];
//新建连续存储空间:类型确定为SElemType, 其大小为 MAXSIZE(10000) 然后赋值给栈底指针
if (!S.base)
return OVERFLOW;
//分配失败返回 -2
S.top = S.base;
//把base栈底指针的地址也赋值给栈顶指针
S.stacksize = MAXSIZE;
//赋值栈的容量
return OK;
}
//我们给一个初始化的函数构造(操作数栈初始化)
Status InitStackN ( SqStack_opnd &S )
{
S.base = new NElemType[MAXSIZE];
//新建连续存储空间:类型确定为SElemType, 其大小为 MAXSIZE(10000) 然后赋值给栈底指针
if (!S.base)
return OVERFLOW;
//分配失败返回 -2
S.top = S.base;
//把base栈底指针的地址也赋值给栈顶指针
S.stacksize = MAXSIZE; //赋值栈的容量
return OK;
}
//运算符插入操作
Status PushS( SqStack_optr &S, SElemType e)
{
if( S.top - S.base == S.stacksize ) //栈满
return ERROR;
*S.top ++ = e;
return OK;
}
//操作数插入操作
Status PushN( SqStack_opnd &S, NElemType e)
{
if( S.top - S.base == S.stacksize ) //栈满
return ERROR;
*S.top ++ = e;
return OK;
}
//运算符栈出栈操作
Status PopS( SqStack_optr &S, SElemType &e)
{
if( S.top == S.base ) // 栈变空
return ERROR;
e= *--S.top;
return OK;
}
//操作数栈出栈操作
Status PopN( SqStack_opnd &S, NElemType &e)
{
if( S.top == S.base ) // 栈变空
return ERROR;
e= *--S.top;
return OK;
}
//运算符栈取栈顶元素
Status GetTopS( SqStack_optr S)
{
SElemType e;
if(S.top==S.base)
return ERROR;
e=*(S.top-1);
return e;
}
//操作数栈取栈顶元素
Status GetTopN( SqStack_opnd S)
{
NElemType e;
if(S.top==S.base)
return ERROR;
e=*(S.top-1);
return e;
}
//判断 字符c 是否为运算符,如果是运算符,那么返回TRUE。如果不是运算符,那么返回FALSE
Status In(SElemType c)//应在前面有定义 typedef char SElemType
{
switch(c)
{ //逐个判断其是否为运算符
case '+':return TRUE;
case '-':return TRUE;
case '*':return TRUE;
case '/':return TRUE;
case '%':return TRUE;
case '(':return TRUE;
case ')':return TRUE;
case '#':return TRUE;
default: return FALSE;
}
}
//判断运算符的优先级,如果 运算符1 大于 运算符2, OPND出栈,运算符1出栈,完成运算后压栈OPND
//如果 运算符1 小于 运算符2, 运算符2压栈
//如果 运算符1 等于 运算符2,脱括号
SElemType Precede(SElemType t1,SElemType t2)
{ //细心观察其优先级顺序
SElemType f;
switch (t2)
{
case '+': if(t1=='('||t1=='#')
f='<';
else
f='>';
break;
case '-': if(t1=='('||t1=='#')
f='<';
else
f='>';
break;
case '*': if(t1=='('||t1=='#'||t1=='+'||t1=='-')
f='<';
else
f='>';
break;
case '/': if(t1=='('||t1=='#'||t1=='+'||t1=='-')
f='<';
else
f='>';
break;
case '%': if(t1=='('||t1=='#'||t1=='+'||t1=='-')
f='<';
else
f='>';
break;
case '(': if(t1=='=')
f='=';
else
f='<';
break;
case ')': if(t1=='('||t1=='#')
f='=';
else
f='>';
break;
case '#': if(t1=='('||t1=='#')
f='=';
else
f='>';
break;
}
return f;
}
//
NElemType Operate(NElemType a,SElemType theta,NElemType b)
{
NElemType c;
switch(theta)
{
case '+':
c=a+b;
break;
case '-':
c=a-b;
break;
case '*':
c=a*b;
break;
case '/':
c=a/b;
break;
case '%':
c=a%b;
break;
}
return c;
}
//主要运算用的程序(个位数)
int EvaluateExpressionOne(){
SqStack_opnd OPND;
SqStack_optr OPTR;
int a,b;
char ch,x,theta;
InitStackS(OPTR);
PushS(OPTR,'#');
InitStackN(OPND);
cin>>ch;
while(ch!='#'||GetTopS(OPTR) != '#')
{
if(!In(ch))
{
PushN(OPND,ch-'0');
cin>>ch;
} //如果ch不是运算符,则让其进栈
else
switch (Precede(GetTopS(OPTR),ch)) //我们来开始比较优先权
{
case '<': //当前字符ch压入OPTR栈,读入下一字符ch
PushS(OPTR,ch);
cin>>ch;
break;
case '>': //弹出OPTR栈顶的运算符运算,并将运算结果入栈
PopS(OPTR,theta);
PopN(OPND,b);
PopN(OPND,a);
PushN(OPND,Operate(a,theta,b));
break;
case '=': //脱括号并开始接受下一个字符的信息
PopS(OPTR,x);
cin>>ch;
break;
}
} return GetTopN(OPND);
}
//主要运算用的程序(多位数)
int EvaluateExpressionPlural(){
SqStack_opnd OPND;
SqStack_optr OPTR;
int a,b,t,e;
char ch,x,theta;
InitStackS(OPTR);
PushS(OPTR,'#');
InitStackN(OPND);
cin>>ch;
int isnum=0; //对位数进行判断
while(ch!='#'||GetTopS(OPTR) != '#')
{
if(!In(ch))
{
if(isnum==1)
{
PopN(OPND,e);
t=ch-'0';
PushN(OPND,e*10+t);
isnum=1;
cin>>ch;
}
else{
PushN(OPND,ch-'0');
isnum=1;
cin>>ch;
}
} //如果ch不是运算符,则让其进栈
else
{
isnum=0;
switch (Precede(GetTopS(OPTR),ch)) //我们来开始比较优先权
{
case '<': //当前字符ch压入OPTR栈,读入下一字符ch
PushS(OPTR,ch);
cin>>ch;
break;
case '>': //弹出OPTR栈顶的运算符运算,并将运算结果入栈
PopS(OPTR,theta);
PopN(OPND,b);
PopN(OPND,a);
PushN(OPND,Operate(a,theta,b));
break;
case '=': //脱括号并开始接受下一个字符的信息
PopS(OPTR,x);
cin>>ch;
break;
}
} //else
} return GetTopN(OPND);
}
//这是为小数计算准备的新方法
Write_stack<char>OPTR_new;
Write_stack<double>OPND_new;
typedef struct
{//因为在输入表达式时是一个一个的输入字符,而操作数若长度大于2则无法正确得到操作数
char data[10];
int length;
}NumberSaving;
bool In_new(char ch)//判断输入的是不是运算符,若是则返回true
{
if (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '(' || ch == ')'||ch=='#')
{
return true;
}
else return false;
}
char Precede_new(char x, char y)//运算符优先级的判断
{
int Square[2];
char opnd[2] = { x,y };
char data_data[7][7] = {
{'>','>','<','<','<','>','>'},
{'>','>','<','<','<','>','>'},
{'>','>','>','>','<','>','>'},
{'>','>','>','>','<','>','>'},
{'<','<','<','<','<','=',' '},
{'>','>','>','>',' ','>','>'},
{'<','<','<','<','<',' ','='}
};
for (int count = 0; count < 2; count++)
{
switch (opnd[count])
{
case '+':Square[count] = 0;
break;
case '-':Square[count] = 1;
break;
case '*':Square[count] = 2;
break;
case '/':Square[count] = 3;
break;
case '(':Square[count] = 4;
break;
case ')':Square[count] = 5;
break;
case '#':Square[count] = 6;
break;
}
}
return data_data[Square[0]][Square[1]];
}
double Operate_new(double a, char theta, double b)//加减乘除运算
{
double result;
switch (theta)
{
case '+': result=a + b;
break;
case '-':result= a - b;
break;
case '*':result= a * b;
break;
case '/':result= a / b;
break;
}
return result ;
}
double CharForNum(NumberSaving NumSave)//将字符数字转换成一个完整的数字型数字
{
int point=0;//用来判断是否有小数点以及小数点的位置
double result=0;
for (int i = 0; i < NumSave.length; i++)//先找出小数点的位置
{
if (NumSave.data[i] == '.')
break;
++point;
}
for (int i=0,j = 0; i < NumSave.length; i++)//根据结构中的length以及小数点位置可以正确转换数字
{
if (i != point)//小数点不放入计算
{
result += (NumSave.data[i] - '0')*pow(10, point - j - 1);
j++;
}
}
return result;
}
double EvaluateExpressionPoint()
{
bool judge = true;//用来判断是否输入了一个数字。如果输入完成了一个数,则让其转化为数字型,存入栈中
NumberSaving NumSave;//用来记录字符,转换为数字
OPTR_new.Push('#');//将表达式起始符压入栈中,用于进行判断
char ch,theta;//记录
int i=0;
double a, b;//记录
cout<<"请输入需要计算的表达式,以'#'结束"<<endl;
cin >> ch;
NumSave.length = 0;
while (ch != '#' || OPTR_new.Top() != '#')// 不能将ch=='#'直接认定为停止循环,因为OPTR中还有加减乘除需要运算
{
if (!In_new(ch))
{
NumSave.data[i] = ch;
++i;
++NumSave.length;
judge = true;
cin >> ch;
}
else {
if (judge)
{
OPND_new.Push(CharForNum(NumSave));//字符转换数字
NumSave.length = 0; i = 0;//所有回到初始化,用以记录下一个数字
judge = false;
}
switch (Precede_new(OPTR_new.Top(), ch))
{
case '>': {
theta = OPTR_new.Top();
OPTR_new.Pop(); //如果栈顶运算符优先级较大,则取OPND栈顶两个数以及OPTR栈顶运算符进行运算,结果再入栈
b= OPND_new.Top();
OPND_new.Pop();
a= OPND_new.Top();
OPND_new.Pop();
OPND_new.Push(Operate_new(a, theta, b));
break;
}
case '<': {
OPTR_new.Push(ch);
cin >> ch;
break;
}
case '=': {
OPTR_new.Pop();
cin >> ch;
break;
}
}
}
}
return OPND_new.Top();
}
//我们再引入第三种方法
string charactor;
char symbol[6] = { '!','&','|','(',')','#' };
char order[6][6] =
{
'>', '>', '>', '<', '>', '>',
'<', '>', '>', '<', '>', '>',
'<', '<', '>', '<', '>', '>',
'<', '<', '<', '<', '=', '>',
'>', '>', '>', '<', '>', '>',
'<', '<', '<', '<', '>', '=',
};
int findsetting(char option)
{
for (int i = 0; i < 6; i++)
{
if (symbol[i] == option)
return i;
}
}
char compare(char a, char b)
{
int x = findsetting(a);
int y = findsetting(b);
return order[x][y];
}
int calculator(int x, int y, char option)
{
if (option == '|')
return x | y;
if (option == '&')
return x&y;
return 0;
}
int EvaluateExpressionLogic()
{
getchar ();
while (getline(cin, charactor))
{
int lens = charactor.size();
charactor += "#";
charactor += '\0';
int t, x, y;
char option;
int i = 0;
Write_stack<int>P;
Write_stack<char>Q;
Q.Push('#'); //末尾符号“#”
while (charactor[i] != '#' || Q.Top() != '#')
{
if (charactor[i] == '1' || charactor[i] == '0')
{
if (charactor[i] == '1')
t = 1;
else
t = 0;
P.Push(t);
i++;
}
else if (charactor[i] != ' ')
{
switch (compare(Q.Top(), charactor[i]))
{
case '<':
Q.Push(charactor[i]);
i++;
break;
case '=':
Q.Pop();
i++;
break;
case '>':
if (Q.Top() == '!')
{
x = P.Top();
P.Pop();
P.Push(!x);
Q.Pop();
}
else
{
x = P.Top();
P.Pop();
y = P.Top();
P.Pop();
option = Q.Top();
Q.Pop(); //计算结果并入栈
P.Push(calculator(x, y, option));
}
break;
}
}
}
int target = P.Top();
cout << target;
}
return 0;
}
//这是其中的第四种方法
string format(string str_one){
for(int i = 0;i < str_one.length(); i++){
if(str_one[i] == '-')
{
if(i == 0)
{
str_one.insert(0,1,'0');
}else if(str_one[i-1] == '('){
str_one.insert(i,1,'0');
}
}
}
return str_one;
}
int orderone(char c){
if(c == '+' || c == '-'){
return 1;
}else if(c == '*' || c == '/'){
return 2;
}else{
return 0;
}
}
vector<string> horizon(string str_one){
vector<string> visual;
Write_stack<char> Des_one;
for(int i = 0;i < str_one.length(); i++){
string tmp = "";
switch(str_one[i]){
case '+':
case '-':
case '*':
case '/':
if(Des_one.Empty() || Des_one.Top() == '('){
Des_one.Push(str_one[i]);
}else{
while(!Des_one.Empty() && orderone(Des_one.Top()) >= orderone(str_one[i]) ){
tmp += Des_one.Top();
visual.push_back(tmp);
Des_one.Pop();
tmp = "";
}
Des_one.Push(str_one[i]);
}
break;
case '(':
Des_one.Push(str_one[i]);
break;
case ')':
while(Des_one.Top() != '('){
tmp += Des_one.Top();
visual.push_back(tmp);
Des_one.Pop();
tmp = "";
}
Des_one.Pop();
break;
default:
if((str_one[i]>='0' && str_one[i]<='9')){
tmp += str_one[i];
while(i+1<str_one.size() && str_one[i+1]>='0' && str_one[i+1]<='9'||str_one[i+1] == '.')
{
tmp += str_one[i+1];
++i;
}
visual.push_back(tmp);
}
}
}
while(!Des_one.Empty()){
string tmp = "";
tmp += Des_one.Top();
visual.push_back(tmp);
Des_one.Pop();
}
return visual;
}
double result(vector<string> bh){
Write_stack<double> Des_two;
double num,op1,op2;
for(int i = 0;i < bh.size(); i++){
string tmp = bh[i];
if(tmp[0] >= '0'&&tmp[0] <= '9'){
num = atof(tmp.c_str());
Des_two.Push(num);
}
else if(bh[i]=="+")
{
op2=Des_two.Top();
Des_two.Pop();
op1=Des_two.Top();
Des_two.Pop();
Des_two.Push(op1+op2);
}
else if(bh[i]=="-")
{
op2=Des_two.Top();
Des_two.Pop();
op1=Des_two.Top();
Des_two.Pop();
Des_two.Push(op1-op2);
}
else if(bh[i]=="*")
{
op2=Des_two.Top();
Des_two.Pop();
op1=Des_two.Top();
Des_two.Pop();
Des_two.Push(op1*op2);
}
else if(bh[i]=="/")
{
op2=Des_two.Top();
Des_two.Pop();
op1=Des_two.Top();
Des_two.Pop();
Des_two.Push(op1/op2);
}
}
return Des_two.Top();
}
void solve(string str_one){
str_one = format(str_one);
vector<string> bh = horizon(str_one);
double k = result(bh);
if((int)k == k){
cout<<k<<endl;
} else{
printf("%.1f",k);
cout<<endl;
}
}
int EvaluateExpressionLess(){
string str_one;
getchar();
// getchar();
while(getline(cin,str_one)){
solve(str_one);
}
}
int main()
{
cout<<"请输入您想选择的模式:"<<"\n"<<"注意:基础运算模式包括+、-、*、/、%\n“a”为个位数运算(基础版)\n“b”为个位数及以上运算(优化版)"<<endl;
cout<<"“c”为小数专用运算(升级版)"<<"\n“d”为逻辑运算(强化版)"<<endl;
cout<<"“e”为负数专用运算(高级版)"<<endl;
char choose;
cin>>choose;
if(choose=='a')
{
cout<<"请输入算术表达式,并以#结束。"<<endl;
cout<<"该运算的表达结果为:"<<EvaluateExpressionOne()<<endl;
}
if(choose=='b')
{
cout<<"请输入算术表达式,并以#结束。"<<endl;
cout<<"该运算的表达结果为:"<<EvaluateExpressionPlural()<<endl;
}
if(choose=='c')
{
cout<<"该运算的表达结果为:"<<EvaluateExpressionPoint();
}
if(choose=='d')
{
cout<<"请输入你想计算的逻辑表达式,并以#结束。"<<endl;
cout<<"该运算的表达结果为:"<<EvaluateExpressionLogic()<<endl;
}//还需要修改
if(choose=='e')
{
cout<<"请输入你想计算的含负数运算表达式,并以#结束。"<<endl;
cout<<"该运算的表达结果为:"<<EvaluateExpressionLess()<<endl;
}
return 0;
}
----以上为本文的所有内容,万望学术长青。