数据结构:重言式判别

题目:设计一个程序,通过真值表判别一个逻辑表达式属于重言式,矛盾式,或二者都不是,并显示表达式真值表。

一、需求分析

  1. 逻辑表达式从终端输入,长度不超过一行。逻辑运算符包括“|”,“&”和“~”,分别表示或,与和非,运算优先程度递增,但可由括号改变,即括号内运算优先。逻辑变元为大写字母。表达式中任何地方都可以含有一个或多个空格符。

  2. 若是重言式或矛盾式,可以只显示“True forever”或“False forever”,否则显示“Satisfactible”以及变量名序列,与用户交互。若用户对表达式中变元取定一组值,程序就求出并显示逻辑表达式的值。。

  3. 测试数据

(1)(A|~A) & (B|~B)

(2)(A&~A)&C

(3)A|B|C|D|E|~A

(4)A&B&C&~B

(5)(A|B)&(A|~B)

(6)A&~ B|~A&B;0,0;0,1;1,0;1,1;

二、概要设计

1. 数据结构

二叉树的抽象数据类型定义如下:

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上的关系H_l \subset H ,若Dr≠∅,则Dr中一定存在唯一的元素xr,<root,xr>∈H,且存在Dr上的关系H_r \subset H ;H={<root,xl>,<root,xr>,Hl,Hr};

(4)(Dl,{Hl})是一颗符合基本定义的二叉树,称为根的左子树,(Dr,{Hr})是一颗符合基本定义的二叉树,称为根的右子树。

基本操作P:

CreatBiTree(&T, definition)

初始条件:defination给出二叉树的定义

操作结果:按definition构造二叉树T

Assign(T,&e,value)

初始条件:二叉树T存在,e是T中某个节点

操作结果:节点e赋值为value

PreOrderTraverse(T,Visit())

初始条件:二叉树存在,Visit是对每个节点操作的应用函数

操作结果:先序遍历T,对每一个节点调用函数Visit一次且仅一次

PostOrderTraverse(T,Visit())

初始条件:二叉树存在,Visit是对每个节点操作的应用函数

操作结果:后序遍历T,对每一个节点调用函数Visit一次且仅一次

}

以栈作为辅助,数据类型为:

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为新的栈顶元素

}

2. 使用函数

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

三、详细设计

1. 数据储存结构

二叉树的二叉链表储存结构和辅助栈的实现如下:

typedef struct BiTNode{
    char data;      // 元素名称
    int value;      // 0,1值
    struct BiTNode *lchild,*rchild;
}*BiTree;

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

2. 计算功能实现

  • 识别逻辑表达式符号形式并建立二叉树
    采用自底向上的算符优先法,参考教科书3.2.5节
int CreatBiTree(BiTree &B, char *a){
    // 自底向上的算符优先算法建立二叉树。OPTR和OPND分别为运算符栈和运算数栈
    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 AssignValue(BiTree B,char c,int value){     // 先序遍历赋值
    if(B){
        if(B->data==c) B->value=value;
        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 CustomAssign(BiTree b, char ch[], char a[]){    // 用户赋值计算表达式
    printf("Assign values manually?(Y/N)\n");
    char c;
    c=getchar();
    if(c=='Y' || c=='y'){
        int k=0,x;
        while(ch[k] != '\0'){
            printf("%c=", ch[k]);
            scanf("%d", &x);
            while(x!=0 && x!=1){
                printf("Error!0 and 1 only:");
                scanf("%d", &x);
            }
            AssignValue(b, ch[k], x);
            k++;
        }
        Calculate(b);
        printf("The value of ");
        Show(a);
        printf(" is %d\n", b->value);
    }
    return 1;
}
  • 循环计算所有命题下表达式取值
int Evaluate(BiTree B, int kind, char *c, int *res){    
// 2^n循环计算所有命题下表达式取值
    int comb[20];
    int i,j,k,l;
    int num;
    num = pow(2,double(kind));
    for(k=0; k<num; k++){
        for(i=0; i<kind; i++){
            comb[i] = 0;
        }
        Num2Bin(comb, k, kind-1);
        for(j=0; j<kind; j++){
            AssignValue(B, c[j], comb[j]);
        }
        Calculate(B);
        res[k] = B->value;
        for(l=0; l<kind; l++){
            printf("%d ",comb[l]);
        }
        printf("| %d\n",res[k]);
    }
    return 1;
}

3. 主程序

首先判断输入表达式是否合法,决定是否构建二叉树进行计算

int main(){
    int res[1024];  // 真值表
    int kind;   // 变量数
    BiTree b;
    char a[100],ch[20]; // 表达式和变量名
    for(int m=0; m<1024; m++){
        res[m]=-1;
    }
    for(int k=0; k<20; k++){
        ch[k]='\0';
    }
    gets(a);
    if(!CorrectNot(a)){
        int i = 0;
        kind = Getvar(a, ch);
        CreatBiTree(b,a);
        printf("Truth Table:\n");
        while(ch[i]!='\0'){
            printf("%c ",ch[i]);
            i++;
        }
        printf("| ");
        Show(a);
        printf("\n");
        Evaluate(b, kind, ch, res);
        switch(Judge(res)){
            case 'T':
                printf("True forever\n");
                break;
            case 'F':
                printf("False forever\n");
                break;
            case 'S':
                printf("Satisfactible\n");
                CustomAssign(b,ch,a);
                break;
        }
    }else{
        printf("expression error!");
    }
    return 1;
}

4. 程序的层次结构

层次结构

四、用户手册

  1. 本程序的运行环境为DOS操作系统,执行文件为:logic.exe

  2. 进入程序按提示操作,输入表达式

  3. 输入后按回车符即显示结果

  4. 非矛盾和重言式可以选择自行赋值,赋值后显示逻辑表达式值

五、测试结果

(1)(A|~A) & (B|~B)

(2)(A&~A)&C

(3)A|B|C|D|E|~A

(4)A&B&C&~B

(5)(A|B)&(A|~B)

(6)A&~ B|~A&B;0,0;0,1;1,0;1,1;

测试结果

六、源代码

#include<string.h>
#include<stdio.h>
#include<malloc.h>
#include<math.h>

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 1;
}

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

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

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


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构造二叉树,OPTR和OPND分别为运算符栈和运算数栈
    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 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 AssignValue(BiTree B,char c,int value){
    // 先序遍历二叉树,为data为c节点的赋值value
    if(B){
        if(B->data==c) B->value=value;
        AssignValue(B->lchild, c, value);
        AssignValue(B->rchild, c, value);
    }
    return 1;
}

int Show(char *a){
    // 打印表达式
    int i=0;
    while(a[i]!='#'){
        if(a[i]==' '){i++; continue;}
        printf("%c",a[i]);
        i++;
    }
    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 kind, char *c, int *res){
    //数组res返回二叉树的真值表
    int comb[20];
    int i,j,k,l;
    int num;
    num = pow(2,double(kind));
    for(k=0; k<num; k++){
        for(i=0; i<kind; i++){
            comb[i] = 0;
        }
        Num2Bin(comb, k, kind-1);
        for(j=0; j<kind; j++){
            AssignValue(B, c[j], comb[j]);
        }
        Calculate(B);
        res[k] = B->value;
        for(l=0; l<kind; l++){
            printf("%d ",comb[l]);
        }
        printf("| %d\n",res[k]);
    }
    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;
}

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';
}

int CustomAssign(BiTree b, char ch[], char a[]){
    //手动对表达式a中变量ch赋值
    printf("Assign values manually?(Y/N)\n");
    char c;
    c=getchar();
    if(c=='Y' || c=='y'){
        int k=0,x;
        while(ch[k] != '\0'){
            printf("%c=", ch[k]);
            scanf("%d", &x);
            while(x!=0 && x!=1){
                printf("Error!0 and 1 only:");
                scanf("%d", &x);
            }
            AssignValue(b, ch[k], x);
            k++;
        }
        Calculate(b);
        printf("The value of ");
        Show(a);
        printf(" is %d\n", b->value);
    }
    return 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'))) {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'))) {flag = 1;break;}
                if(a[i]!='~'){
                    if(!(a[l]==')'|| (a[l]>='A' && a[l]<='Z'))) {flag = 1;break;}
                }
                if(a[i]=='~'){
                    if((a[l]==')'|| (a[l]>='A' && a[l]<='Z'))) {flag = 1;break;}
                }
            }
            if(a[i]>='A' && a[i]<='Z'){
                if((a[r]>='A' && a[r]<='Z')|| (a[l]>='A' && a[l]<='Z')){flag=1; break;}
            }
        }
        i++;
    }
    if(cnt1 != cnt2) flag = 1;
    return flag;
}

int main(){
    int res[1024];
    int kind;
    int flag = 0;
    BiTree b;
    char a[100],ch[20];
    for(int m=0; m<1024; m++){
        res[m]=-1;
    }
    for(int k=0; k<20; k++){
        ch[k]='\0';
    }
    printf("Input expression:\n");
    gets(a);
    if(!CorrectNot(a)){
        int i = 0;
        kind = Getvar(a, ch);
        CreatBiTree(b,a);
        printf("Truth Table:\n");
        while(ch[i]!='\0'){
            printf("%c ",ch[i]);
            i++;
        }
        printf("| ");
        Show(a);
        printf("\n");
        Evaluate(b, kind, ch, res);
        switch(Judge(res)){
            case 'T':
                printf("True forever\n");
                break;
            case 'F':
                printf("False forever\n");
                break;
            case 'S':
                printf("Satisfactible\n");
                CustomAssign(b,ch,a);
                break;
        }
    }else{
        printf("expression error!");
    }
    return 1;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容

  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,714评论 0 38
  • 专业考题类型管理运行工作负责人一般作业考题内容选项A选项B选项C选项D选项E选项F正确答案 变电单选GYSZ本规程...
    小白兔去钓鱼阅读 8,970评论 0 13
  • 本文是基于单生产者单消费者线程的实现。 struct { char buf[65536]; unsigned sh...
    银角代王阅读 2,060评论 0 49
  • 我该做个不动声色的人了,对梦想自己暗暗发誓就好,对不喜欢的人慢慢远离就好。没有人会时刻关心我情绪的变化,内心的彷徨...
    赢叶阅读 136评论 0 0
  • 《鬼吹灯之寻龙诀》最近成为了热门话题 上映之后好评如潮,在朋友圈、微博疯狂刷屏 这就来揭秘一下,《寻龙诀》里那些绝...
    背包旅行阅读 1,080评论 0 0