重言式判别

本文介绍如何判断逻辑表达式是重言式、矛盾式还是两者都不是,通过建立二叉树来处理逻辑表达式。文章讨论了自底向上和自顶下两种方法建立二叉树,并详细解释了先序遍历的优势。作者实现了一个递归算法构建二叉树,并探讨了计算逻辑表达式值的方法,最后分享了他人的简洁解决方案,包括加权字符数组和二进制字符串来产生真值表。
摘要由CSDN通过智能技术生成
  • 原题参见严蔚敏版《数据结构题集(C语言版)》P148 5.1
  • 简单来说,就是写一个程序判断类似于(A|~A)&(B|~B)的逻辑表达式是重言式,矛盾式,还是两者都不是。

一、杂乱的知识点笔记

识别逻辑表达式的符号形式并建立二叉树有两种策略:自底向上的算符优先法和自顶向下分割,先序遍历建立二叉树的方法。

  1. 自底向上的算符优先法:我个人的理解是,类比通过栈计算算术表达式的算法。一个栈负责存叶子节点,一个栈负责存逻辑运算符。找到两个叶子节点,同时找到当前优先级相对高的符号,组成一棵子树,然后将这棵子树的根节点作为一个新的叶子节点。不断出栈入栈,最后形成一棵代表逻辑表达式的二叉树。运算符之间的优先级关系可以用一个写定的二维数组表示。
  2. 自顶向下分割,先序遍历建立二叉树的方法:例如(A|~A)&(B|~B),其先序序列可以为“& | A ~ A | B ~ B”,如果能由逻辑表达式得到这样一个对应的先序序列,然后按照先序遍历建成二叉树就行。
  3. 为什么要采用二叉树去表示逻辑表达式?:如果是算数表达式,用二叉树来表示就会显得非常繁琐,讲道理,一边处理就可以一边计算了。反观逻辑表达式,它和算术表达式的不同就很能说明问题,逻辑表达式里的一个命题A,可以为TRUE或FALSE,为了证明这个表达式是否为重言式,就得来回给表达式中的命题带入TRUE或FALSE计算结果,这时候将逻辑表达式建立为一个二叉树就能很方便处理:通过递归。
  4. 先,中,后序三种遍历中,用后序序列遍历计算最好:例如(A|~A)&(B|~B),假如它的先,中,后序序列分别是:“& | A ~ A | B ~ B”、“A | ~ A & B | ~ B”、“A A ~ | B B ~ | &”。首先,因为括号是不会出现在二叉树中的,中序序列是没法体现出之前括号给定的优先级关系,所以中序不适合计算。先序和后序能体现括号给定的优先级。先序序列需要从右往左找运算符计算,比如B和~B要向左找‘|’,而后序是从左往右寻,先A,第二个A做‘~’运算,然后前面两个做‘|’运算。显然从左往右更符合我们日常计算的习惯。

二、混乱的大致实现思路

  1. 首先要解决的问题是,怎么用先序遍历的方式建立二叉树?我考虑过能不能写一个函数将逻辑函数字符串转换成先序序列,然后直接用之前写的先序建立一个二叉树。然后我发现这个方式有两个难点:括号的存在对符号优先级的影响;单纯一个先序不足以建立一棵二叉树。对此毫无思路的我,看到了书中的提示:

    以二叉树表示表达式的递归定义如下:若表达式为数或简单的变量,则相应二叉树中仅有一个根结点,其数据域存放该表达式信息;若表达式=(第一操作数)(运算符)(第二操作数),则相应的二叉树中以左子树表示第一操作数;右子树表示第二操作数;根结点的数据域存放运算符(若为一元运算符,则左子树为空)。操作数本身又为表达式。

  2. 然后我就打算强行写一个递归函数,每次调用的目的在于寻找第一操作数,运算符和第二操作数,将这三个组合成一棵子二叉树,并返回运算符(根节点)的指针。写的时候发现有很多特殊情况要处理,然后就补充了大量的if语句。最后完成并调试很多遍后,虽然书中的例子我都输入通过了,我依旧觉得我这个算法有问题,不完善。

  3. 在建立好二叉树后,考虑如何计算表达式的值并证明该式子是否为重言式。我的想法是去维护一个一维数组,这个数组记录了所有命题和命题的取值(0 or 1,也就是FALSE还是TRUE),每次计算都会去查询这个数组,当前计算的命题对应的取值。我考虑过指针数组,但感觉逻辑表达式中命题经常重名,命题相同但在二叉树中的节点位置不同,存储起来也很麻烦,还不如一维数组加查找。

  4. 最后证明逻辑表达式是否为重言式,意味着我很有可能要产生一个真值表,产生一个真值表就意味着我需要在3中提到一维数组中做0和1的全排列,我唯一想到的方法是用递归,每次进入递归前改一下。

  5. 最后的最后我偷懒没写释放树申请空间的代码。

三、不靠谱的代码实现

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX_A_LEN 30
#define MAX_S_NUM 20

typedef struct LSNode{
  //二叉树节点
    char f;
    int b;
    struct LSNode* LSubNode;
    struct LSNode* RSubNode;
}LSNode;

typedef struct CC{
    char f;
    int b;
}CC;
typedef struct CCTABLE{
  //对应命题的一维数组的结构定义
    CC count[MAX_S_NUM];
    int total;
}CCTABLE;

LSNode*  CLS(char* LogicStatent,int length,char Prec,CCTABLE* C);
LSNode* CreateNode(char c,CCTABLE* C);
void freeTree(LSNode* T);//偷懒没写的释放申请空间函数
int Judge_S(LSNode* T,CCTABLE* C,int mark);
int Caculate_S(LSNode* T,CCTABLE* C);

int main()
{
    int slu;
    LSNode* HEAD;
    CCTABLE* C;
    C=malloc(sizeof(CCTABLE));
    C->total=0;

    char LogicStatent[MAX_A_LEN];
    memset(LogicStatent,'\0',MAX_A_LEN);
    gets(LogicStatent);

    HEAD=CLS(LogicStatent,strlen(LogicStatent),'\0',C);

    slu=Judge_S(HEAD,C,0);
    if(slu==-1){
        printf("Satisfactible\n");
    }
    else if(slu==0){
        printf("False forever\n");
    }
    else if(slu==1){
        printf("True forever\n");
    }

    return 0;
}

int Judge_S(LSNode* T,CCTABLE* C,int mark)//判断函数,返回值-1代表可变式,0为矛盾式,1为重言式 
{
    int slu;
    static int preslu=-2;
    if(mark==C->total){
        slu=Caculate_S(T,C);
        if(preslu==-2) preslu=slu;
        else if(slu!=preslu){
            preslu=-1;
        }   
    } 
    else{
        C->count[mark].b=0;
        Judge_S(T,C,mark+
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值