杭电数据结构课程实践-重言式判别

需求分析

  一个逻辑表达式如果对于其变元的任一种取值都为真,则称重言式;反之,如果对于其变元的任一种取值都为假,则称矛盾式;其他情形称为可满足式。编写程序,判断逻辑表达式属于哪种情形。

概要设计

二叉树的抽象数据类型定义如下:
ADT BinaryTree{
数据对象D:D是具有相同特性的数据元素的集合。
数据关系R
若D=∅,则R=∅,称BinaryTree为空二叉树;
若D≠∅,则R={H},H是如下二元关系;
(1)在D中存在唯一的称为根的数据元素root,它在关系H下无前驱;
(2)若D-{root}≠∅,则存在D-{root}={Dl,Dr},且Dl∩Dr=∅
(3)若Dl≠∅,则Dl中一定存在唯一的元素xl,<root,xl>∈H,且存在D1上的关系H1 ⊂ H ,若Dr≠∅,则Dr中一定存在唯一的元素xr,<root,xr>∈H,且存在Dr上的关系Hr ⊂ H ;H={<root,xl>,<root,xr>,Hl,Hr};
(4)(Dl,{Hl})是一颗符合基本定义的二叉树,称为根的左子树,(Dr,{Hr})是一颗符合基本定义的二叉树,称为根的右子树。
基本操作P:
CreatBiTree(&T, definition)
初始条件:defination给出二叉树的定义
操作结果:按definition构造二叉树T
Assignvalue(T,&e,value)
初始条件:二叉树T存在,e是T中某个节点
操作结果:节点e赋值为value
PostOrderTraverse(T,Visit())
初始条件:二叉树存在,Visit是对每个节点操作的应用函数
操作结果:后序遍历T,对每一个节点调用函数Visit一次且仅一次
}// ADT BinaryTree

以栈作为辅助,数据类型为:
ADT Stack{
数据对象:
D={ai|a∈EleSet,i=1,2,…,n,n≥0}
数据关系:
R1={<ai-1,ai>|ai-1,ai∈D,i=2,…,n}
约定an为栈顶,a1为栈底。
基本操作:
InitStack(&S)
操作结果:构造一个空栈S
GetTop(S)
初始条件:栈S已存在且非空
操作结果:返回S栈顶元素
Pop(&S, &e)
初始条件:栈S已存在
操作结果:删除S的栈顶元素,并用e返回其值
Push(&S, &e)
初始条件:栈S已存在
操作结果:插入e为新的栈顶元素
}// ADT Stack

使用函数:
int InitStack (BiTStack &S)
操作结果:构造栈存放二叉树节点
int Push (BiTStack &S, BiTree &B)
操作结果:二叉树节点入栈
int Pop(BiTStack &S,BiTree &B)
操作结果:二叉树节点出栈
BiTree Gettop(BiTStack S)
操作结果:返回栈顶元素
int In(char c)
操作结果:判定字符是否为运算符
char Precede(char c1, char c2)
操作结果:判断逻辑运算符的优先级
int CorrectNot(char a[])
操作结果:返回表达式是否合法
int CreatBiTree(BiTree &B, char *a)
操作结果:根据表达式a构造二叉树
int Getvar(char *a,char *ch)
操作结果:返回表达式中不同的变量个数和变量ch
int AssignValue(BiTree B,char c,int value)
操作结果:先序遍历二叉树,为data为c或0,1的节点赋值value或0,1,
int Calculate(BiTree B)
操作结果:后序遍历二叉树,计算表达式真值
int Num2Bin(int *b,int x,int len)
操作结果:数组b返回整数x的二进制表示
int Evaluate(BiTree B, int kind, char *c, int *res)
操作结果:计算二叉树的真值
char Judge(int *res)
操作结果:根据真值表判断表达式为重言式,矛盾式或可满足式
void showoff(char x)
操作结果:显示判断结果

详细设计

  实现重言式判别基本操作的“Tree__Stack.h”文件如下:

#ifndef TREE__STACK_H_INCLUDED
#define TREE__STACK_H_INCLUDED
#endif // TREE__STACK_H_INCLUDED

typedef struct BiTNode{
    char data;  //结点值
    int value;  //参数值
    struct BiTNode *lchild,*rchild; //左右孩子结点指针
}*BiTree;

typedef struct {
    BiTree *top;
    BiTree *base;
}BiTStack;

int InitStack(BiTStack &S){
    //构造栈存放二叉树节点
    S.base=(BiTree*)malloc(sizeof(BiTNode));
    S.top=S.base;
    return OK;
}

int Push(BiTStack &S, BiTree &B){
    //二叉树节点入栈
    *S.top=B;
    S.top++;
    return OK;
}

int Pop(BiTStack &S,BiTree &B){
    //二叉树节点出栈
    S.top--;
    B=*S.top;
    return OK;
}

BiTree Gettop(BiTStack S){  //返回栈顶元素
    return *(S.top-1);
}

int CorrectNot(char a[]){
    //返回表达式是否合法
    int i = 0;
    int flag = 0;
    int cnt1=0;
    int cnt2=0;
    while(a[i]!='\0'){
        if(a[i]=='(') cnt1++;
        if(a[i]==')') cnt2++;
        if(!(a[i]==' ' ||
             a[i]=='~' ||
             a[i]=='|' ||
             a[i]=='&' ||
             a[i]=='(' ||
             a[i]== ')'||
             (a[i]>='A' && a[i]<='Z')||(a[i]>='0' && a[i]<='1'))) {flag = 1; break;}  //不是运算符和运算数
        if(cnt2 > cnt1) {flag = 1; break;}  //括号不匹配
        if(a[i]!=' ' && i !=0){
            int l,r;
            l = i-1;
            r = i+1;
            while(a[l]==' '){l--;}
            while(a[r]==' '){r++;}
            if(a[i]=='~' || a[i]=='|' || a[i]=='&'){
                if(!(a[r]=='~' || a[r]=='(' || (a[r]>='A' && a[r]<='Z')||(a[r]>='0' && a[r]<='1'))) {flag = 1;break;}  
//不是运算数或者不是括号和非
                if(a[i]!='~'){
                    if(!(a[l]==')'|| (a[l]>='A' && a[l]<='Z')||(a[l]>='0' && a[l]<='1'))) {flag = 1;break;}  
//符号前面不是右括号或者运算数
                }
                if(a[i]=='~'){
                    if((a[l]==')'|| (a[l]>='A' && a[l]<='Z')||(a[l]>='0' && a[l]<='1'))) {flag = 1;break;}  
//右括号跟非或者运算数跟非
                }
            }
            if((a[i]>='A' && a[i]<='Z')||(a[i]>='0' && a[i]<='1')){
                if((a[r]>='A' && a[r]<='Z')|| (a[l]>='A' && a[l]<='Z')||(a[r]>='0' && a[r]<='1')||(a[l]>='0' && a[l]<='1')){flag=1; break;} //运算数跟运算数
            }
        }
        i++;
    }
    if(cnt1 != cnt2) flag = 1; //括号不匹配
    return flag;
}

int In(char c){
    //判定字符是否为运算符
    char OP[7] = {'|','&','~','(',')','#','\0'};
    int flag = 0;
    int i = 0;
    while(OP[i] != '\0'){
        if(OP[i] == c) flag=1;
        i++;
    }
    return flag;
}

char Precede(char c1, char c2){
    //判断逻辑运算符的优先级
    char OP[7] = {'|','&','~','(',')','#','\0'};
    unsigned char Prior[7][7] =
    {'x','|','&','~','(',')','#',
     '|','>','<','<','<','>','>',
     '&','>','<','<','<','>','>',
     '~','>','>','>','<','>','>',
     '(','<','<','<','<','=',' ',
     ')','>','>','>','>','>','>',
     '#','<','<','<','<',' ','='
    };
    int i = 0; int j = 0;
    while(c1 != OP[i]) i++;
    while(c2 != OP[j]) j++;
    return Prior[i+1][j+1];
}

int CreatBiTree(BiTree &B, char *a){
    //根据表达式a构造二叉树
    char *expr;
    char End[] = {'#','\0'};
    expr = strcat(a,End);
    BiTStack OPTR, OPND;
    InitStack(OPTR); //运算符栈
    InitStack(OPND);//运算数栈
    BiTree b1,b,x,y,theta;
    b1 = (BiTree)malloc(sizeof(BiTNode));
    b1->data = '#';
    b1->value = 0;
    b1->lchild = NULL;
    b1->rchild = NULL;
    Push(OPTR, b1);
    while(*expr !='#'||Gettop(OPTR)->data!='#'){
        if(*expr ==' '){
            expr++;
            continue;
        }
        b = (BiTree)malloc(sizeof(BiTNode));
        b->data = *expr;
        b->value = 0;
        b->lchild = NULL;
        b->rchild = NULL;
        if(!In(*expr)){
            Push(OPND, b);
            expr++;
            continue;
        }else {
            switch(Precede(Gettop(OPTR)->data, *expr)){
                case '<':
                    Push(OPTR,b);
                    expr++;
                    break;
                case '=':
                    Pop(OPTR, b);
                    expr++;
                    break;
                case '>':
                    Pop(OPTR, theta);
                    Pop(OPND, x);
                    theta->rchild = x;
                    if(theta->data != '~'){
                        Pop(OPND, y);
                        theta->lchild = y;
                    }
                    Push(OPND,theta);
            }
        }
    }
    B=Gettop(OPND);
    return 1;
}

int Getvar(char *a,char *ch){
    //返回表达式中不同的变量个数和变量ch
    int i=0,k=0,flag=1;
    while(a[i]){
        if(a[i]>='A' && a[i]<='Z'){
            int j=0;
            while(ch[j]!='\0'){
                if(ch[j]==a[i]){flag=0; break;}
                j++;
            }
            if(flag){ch[k]=a[i]; k++;}
        }
        i++;
        flag=1;
    }
    return k;
}

int AssignValue(BiTree B,char c,int value){
    // 先序遍历二叉树,为data为c或0,1的节点赋值value或0,1,
    if(B){
        if(B->data==c) B->value=value;
        if(B->data=='1') B->value=1;
        if(B->data=='0') B->value=0;
        AssignValue(B->lchild, c, value);
        AssignValue(B->rchild, c, value);
    }
    return 1;
}

int Calculate(BiTree B){
    // 后序遍历二叉树,计算表达式真值
    if(B){
        Calculate(B->lchild);
        Calculate(B->rchild);
        switch(B->data){
            case '|':
                B->value = B->lchild->value||B->rchild->value;
                break;
            case '&':
                B->value = B->lchild->value&&B->rchild->value;
                break;
            case '~':
                B->value = !B->rchild->value;
                break;
        }
    }
    return 1;
}

int Num2Bin(int *b,int x,int len){
    // 数组b返回整数x的二进制表示
    while(x!=0){
        b[len]=(x%2);
        x=x/2;
        len--;
    }
    return 1;
}

int Evaluate(BiTree B, int blmun, char *c, int *res){
    //计算二叉树的真值
    int comb[20];
    int i,j,k,l;
    int num;
    num = pow(2,double(blmun));
    for(k=0; k<num; k++){
        for(i=0; i<blmun; i++){
            comb[i] = 0;
        }
        Num2Bin(comb, k, blmun-1);
        for(j=0; j<blmun; j++){
            AssignValue(B, c[j], comb[j]);
        }
        Calculate(B);
        res[k] = B->value;
    }
    return 1;
}

char Judge(int *res){
    //根据真值表判断表达式为重言式,矛盾式或可满足式
    int i=0,flag1=0,flag2=0;
    while(res[i] != -1){
        if(res[i] == 0) flag1=1;
        if(res[i] == 1) flag2=1;
        if(flag1 && flag2) return 'S';
        i++;
    }
    if(flag1) return 'F';
    if(flag2) return 'T';
}

void showoff(char x){
    //显示判断结果
    switch(x){
            case 'T':
                printf("该表达式是重言式\n");
                break;
            case 'F':
                printf("该表达式是矛盾式\n");
                break;
            case 'S':
                printf("该表达式是可满足式\n");
                break;
        }
}

  重言式判别主函数“chongyanshi.cpp”文件如下:

#include<string.h>
#include<stdio.h>
#include<malloc.h>
#include<math.h>
#define OK 1
#include"Tree__Stack.h"

int main(){
    int res[1024]; //存放真值表真值
    int blmun; //变量数
    int flag = 0;
    BiTree b;
    char a[100];//存放表达式
    char ch[10];//存放变量
    for(int m=0; m<1024; m++){
        res[m]=-1;
    }  //真值初始化
    for(int k=0; k<20; k++){
        ch[k]='\0';
    }  //表达式初始化
    printf("请输入逻辑表达式:\n\n");
    gets(a);  //获取表达式
    printf("\n");
    if(!CorrectNot(a)){ //表达式合法的情况下
        int i = 0;
        blmun = Getvar(a, ch);//获取变量数以及变量组合
        CreatBiTree(b,a); //根据表达式构建二叉树
        Evaluate(b, blmun, ch, res);// 进行表达式真值计算
        showoff(Judge(res));//根据判断结果返回逻辑表达式类型
    }else{
        printf("表达式不合法");
    }
    return 1;
}

调试分析

  本程序实现了逻辑表达式的类型判断。“Tree__Stack.h”文件定义了带有参数值的二叉树的存储结构,并基于此结构实现了关于逻辑表达式求值的各个基本操作,再根据逻辑表达式的求值结果进行逻辑表达式的类型判定。
  此算法使用了两个工作站OPTR和OPND辅助构建了逻辑表达式二叉树,并通过二叉树的先序遍历为所读取的变量参数进行赋值,再以二叉树的后序遍历计算逻辑表达式二叉树的真值,最后根据其真值进行逻辑表达式的类型判断。
  因为需要对n个运算数的不同可能建立真值表,n个运算数的不同组合共有2n种,所以该算法的复杂度为O(2n)。
  最初程序设计时没有考虑到输入字符为‘0’和‘1’的情况,后对表达式进行了更改。首先更改了判断表达式是否合法的函数,增加了输入是‘0’和‘1’的判断。接着将‘0’和‘1’同样当作是运算数进行建树操作,在遍历赋值时,若该二叉树的结点值为‘0’和‘1’,则自动为其参数值赋值‘0’和‘1’。以此实现了运算数含有布尔类型的逻辑表达式的类型判断。

测试数据及用户手册

  程序经Codeblocks编译器编译,运行环境为windows 10操作系统,进入程序运行后提示用户输入逻辑表达式,之后进行表达式类型判断。请确保逻辑表达式输入正确,否则会提示表达式不合法。
  执行chongyanshi.exe文件,运行结果如下:
在这里插入图片描述

附录

源程序文件名清单:

  1. Tree__Stack.h
  2. chongyanshi.cpp
  • 18
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值