当前位置:文档之家› 逻辑命题公式计算

逻辑命题公式计算

题号:第一题题目:电梯模拟1,需求分析:计算命题演算公式的真值所谓命题演算公式是指由逻辑变量(其值为TRUE或FALSE )和逻辑运算符人(AND )、V( OR)和「( NOT )按一定规则所组成的公式(蕴含之类的运算可以用A、V和「来表示)。

公式运算的先后顺序为「、人、V,而括号()可以改变优先次序。

已知一个命题演算公式及各变量的值,要求设计一个程序来计算公式的真值。

要求:( 1)利用二叉树来计算公式的真值。

首先利用堆栈将中缀形式的公式变为后缀形式;然后根据后缀形式, 从叶结点开始构造相应的二叉树;最后按后序遍历该树, 求各子树之值, 即每到达一个结点, 其子树之值已经计算出来, 当到达根结点时, 求得的值就是公式之真值。

( 2)逻辑变元的标识符不限于单字母,而可以是任意长的字母数字串。

( 3)根据用户的要求显示表达式的真值表。

2,设计:2.1 设计思想:<1> ,数据结构设计:(1) 线性堆栈1 的数据结构定义typedef struct{DataType stack [MaxStackSize];int top; /* 当前栈的表长*/} SeqStack;用线性堆栈主要是用来存储输入的字符, 它的作用就是将中缀表达式变成后缀表达式。

(2) 线性堆栈2 的数据结构定义typedef struct{BiTreeNode *stack [MaxStackSize];int top; /* 当前栈的表长*/} TreeStack;这个堆栈和上面的堆栈的唯一不同就是它们存储的数据的类型不同, 此堆栈存储的是树节点,它的作用是将后缀表达式构成一棵二叉树。

(3)树节点数据结构定义typedef struct Node {DataType data; struct Node *leftChild; struct Node *rightChild; }BiTreeNode;<2>算法设计详细思路如下:首先实现将中缀表达式变成后缀表达式:在将中缀表达式变成后缀表达式的时候会用到堆栈,因此首先需要初始化一个堆栈。

又由于逻辑变元可能是字符也可能是字符串,所以它又不同于将单字符的逻辑变元的中缀表达式变成后缀表达式。

我的设计是这样的,我将中缀表达式变成后缀表达式的过程分成了两部:化简(将一维的复杂的中缀表达式变成一维的简单的中缀表达式,并将字符串逻辑变元存放在二维数组中),转化(将化简后的中缀表达式变成后缀表达式)。

(1)化简:先用一个字符数组存放输入的中缀表达式(表达式以‘#'号结束),然后将一维的中缀表达式中的字符串逻辑变元用一个字符进行标识,这样我们就可以将原来复杂的中缀表达式变成熟悉而又简单的中缀表达式,同时用二维数组存放那些字符串逻辑变元。

实现的过程就是首先扫描一维中缀表达式,如果遇到逻辑符号,那么记住这个逻辑符号在数组中的相对位置用一个变量存放,然后继续扫描中缀表达式直到再次遇到逻辑符号,再一次记住它在中缀表达式中的相对位置,这两个逻辑符号之间的部分就是一个完整的逻辑变元,将这个字符串逻辑变元用一个字符代替并将这个字符串逻辑变元保存在二维数组中。

这个过程的实现我把它放在cha nge()函数中。

(2)转化:在实现该功能时,首先需要定义各符号的优先级,即:'(' 和')' 的优先级最高;'!'(逻辑非号)的优先级次之;'&'(逻辑与号)的优先级又低一级,'|'(逻辑或号)的优先级跟低;'#' (他不是逻辑符号,只是为了方便使用堆栈而设置)的优先级最低,接着将'#'压入堆栈。

在这之后就是正式的转化了,其过程为:当读到的是逻辑变元时直接输出,并保存到保存后缀表达式的数组中,当读到的单词为运算符时,令x1 为当前栈顶运算符的变量,x2 为当前扫描到的简单中缀表达式的运算符的变量,把当前读入的单词赋予变量x2,然后比较x1和x2的优先级。

若x1的优先级高于x2的优先级,将x1退栈作为后缀表达式的一个单词输出,然后接着比较新的栈顶运算符x1 的优先级与x2 的优先级;若x1 的优先级低于x2 的优先级,将x2 的值进栈,然后接着读下一个单词;若x1 的优先级等于x2 的优先级且x1 为“(”,x2 为“)”,将x1 退栈,然后接着读下一个单词;若x1 的优先级等于x2 的优先级且x1 为“#”,x2 为“#”,算法结束。

这个过程我把它放在InToPost()函数中。

然后用后缀表达式构造出二叉树:在这个过程中,我用到了之前所定义的存放树的堆栈。

具体实现为:扫描后缀表达式,如果遇到逻辑变元然后将这个变元变成一个树节点,它的实现就是将该逻辑变元赋给树的data 域,然后将它的左右子树赋为NULL ,然后将这个树节点压入相应的堆栈;接着继续扫描,如果遇到的是单目运算符(非号“!”)也将它构造成一个树节点然后从堆栈里面弹出一个树形节点,将弹出的元素的作为它的左子树,右子树设置为NULL ,然后将这个树节点压入相应的堆栈;如果扫描到的是双目运算符(与号“ & ”或者或号“ |”)将它也构造成一棵树,然后将这个树节点压入相应的堆栈,然后从栈中弹出两个元素,一个作为它的左子树,一个作为它的右子树,如此重复(n n 为后缀表达式的长度)次。

这个过程我把它放在Maketree()函数中。

最后打印真值表:打印真值表也用到了前面的简单的后缀表达式,其实现的基本思想为和构造二叉树差不多,它实现了每到一个根节点就计算一个运算的值。

在打印之前需要统计字符的个数,有m个字符则要打印2A m行,因为他有这么多情况。

具体实现为:用一个字符型的数组来存放每个元素的一次取值,然后扫描后缀表达式,如果遇到逻辑变元就通过这个标识将相应的取值赋给它,然后它压入堆栈;接着继续扫描,如果遇到的是单目运算符(非号“!”)则从堆栈里面弹出一个元素,并对它进行非运算,然后又将这个运算后的值压入堆栈;如果扫描到的是双目运算符(与号“ &”或者或号“ |”)则从栈中弹出两个元素,并对这两个元素进行相应的运算,然后将这个运算后的值压入堆栈,如此重复n (n为后缀表达式的长度)次。

这个过程我把它放在Print()函数中。

其中第一,二过程的流程图描述分别为:2.2设计表示:<1>,函数调用关系及函数说明cha nge()函数原型为:void cha nge(char prefixStr1[],char prefixStr[],i nt len gth[],char store[][10],i nt* row,i nt* k ) 该函数含有有六个参数,其中prefixStr1[]为用户输入的中缀表达式,prefixStr[]为即将转化成为的简单中缀表达式,length[]为二维数组中存放的各个字符串的的长度store[][10]用来存放中缀表达式中的逻辑变元,row就是逻辑变元的个数,k简化后的中缀表达式的长度。

该函数的功能就是将复杂的中缀表达式变成简单的中缀表达式。

InToPost()函数原型为:void InToPost(char prefixStr[],char postfixStr[],SeqStack* myStack,int* n,int k)该函数函数有五个参数prefixStr[]为中缀表达式,postfixStr[]为简化后的后缀表达式,myStack为在转化过程中用到的堆栈,n为后缀表达式的字符长度,k为中缀表达式的字符长度。

该函数的功能就是将简单的中缀表达式变成简单的后缀表达式。

Maketree()函数原型为:void Maketree(BiTreeNode *root,TreeStack* myTree,char postfixStr[],i nt n)该函数共有四个参数,其中root为所建立的树的根节点,myTree是在构造树时所用到的堆栈,另外两个参数和前面的同名参数具有相同意义。

这个函数的功能就是将简单的中缀表达式变成简单的后缀表达式。

Precede。

函数原型为:DataType Precede(DataType x1,DataType x2)该函数有两个参数,返回值类型为DataType型,其中x1和x2就是要进行优先级比较的两个两个字符。

x1为栈顶元素,x2为扫描到的元素。

该函数的功能就是比较x1和x2的优先级并将比较结果返回给调用函数。

Print() 函数原型为:void Print(char postfixStr[],char store[][10],int length[],int row,int n,SeqStack* myStack) 该函数有六个参数,其中myStack 是在输出真值表过程中要用到的堆栈,其余的参数和前面介绍的函数中的同名参数具有相同的意义。

该函数的功能就是打印真值表。

<2> 函数接口说明:函数的调用关系在上面的图中已经显示出来了,整个程序就是由一些子函数的集合,他们之间按所要实现的功能不同而调用不同的函数。

在这些函数中除主函数外,其它函数的级别相同。

2.3 详细设计:(1),定义所需要的结构体,前面已经介绍了; (2),我将中缀表达式变成后缀表达式的过程分成了两部:化简(将一维的复杂的中缀表达式变成一维的简单的中缀表达式,并将字符串逻辑变元存放在二维数组中) ,转化(将化简后的中缀表达式变成后缀表达式)。

其中化简部分将要用伪代码描述为while(prefixStr1[m]!='#') {扫描中缀表达式while(prefixStr1[m] 不为运算'符) { 继续扫描,直到扫描到运算符;}扫描到运算符后{构造简化的中缀表达式;得到这个字符串的长度; 将这个字符串存放在store[][10] 中;}}转化部分用伪代码描述为:循环扫描中缀表达式{if( 扫描到逻辑变元) 保存到后缀表达式中;else{StackTop(myStack,&x);res=Precede(x扫描到的运算符); if(res=='>') x 退栈;if(res=='<') 扫描到的运算符进栈;if(res=='=' && x=='(') 退栈if(res=='=' && x=='#') 结束;}}(3),构造二叉树,其思想就是将逻辑变元作为叶子节点,运算符作为根节点,用堆栈实现,用伪代码简单描述为:循环扫描后缀表达式{if( 扫描到逻辑变元')讲逻辑变元进栈;else{if(' 扫描到双目运算符){StackPop1(myTree,&x1);StackPop1(myTree,&x2);将x1,x2 作为它的左右子树;StackPush1(myTree,p);}else{StackPop1(myTree,&x1);将x1 作为它的左子树,又子树为空;StackPush1(myTree,p);}}StackPop1(myTree,&x1);root->leftChild=x1;}(4),打印二叉树,其基本思想就是每到一个根节点就计算一个值,如此重复,直到总根节点,用伪代码简单描述为:循环赋值{if( 扫描到逻辑变元'){赋值进栈;}else{if( 扫描到双目运算符){从栈中弹出两数对两数进行相应的运算;将运算结构进栈;;}else{从栈中弹出两数;对两数进行非运算;将运算结构进栈;}每循环一次输出一个结构;3,调试分析:<1>,调试过程中遇到的问题与解决方案:这个题我个人觉的是这几个中最麻烦的一个,因为它有几个难点去分析与实现:首先就是中缀表达式中的逻辑变元不是单个字符而是一些字符串,这样在将中缀表达式转化成后缀表达式的时候就会比较麻烦,起初我也不太清楚该怎么处理他,后来经过一番分析与调试后,我觉得用二维数组存放比较好,那样实现起来就会比较简单,当然虽然这么说,其实实现起来也还是有一定的困难的,比如怎样去找到一个完整的字符串逻辑变元,找到之后又怎样存放等等。

相关主题