zoukankan      html  css  js  c++  java
  • 二叉树在表达式中的应用-命题逻辑表达式的真值表计算程序

    对于一个给定的逻辑表达式输出其真值表。具体就是借助二叉树来表示逻辑表达式。

    1、程序说明文档

    本计算程序用于命题逻辑式的计算。
    
    功能概述:按照一定格式(格式稍后说明)输入命题逻辑式,即可得到真值表。
    
    使用说明:
    1、为了方便输入,我们将原本的逻辑运算符号进行了修改。在输入表达式时,请将对应符号转换成我们所要求的符号。下面是对应表列:
    逻辑非替代符!
    合取替代符号*
    析取替代符号/
    蕴含替代符号:
    等价替代符号=
    输入时只需要对公式的符号直接代换输入即可。
    2、运算程序仅支持使用大小写字母表示命题变项,且运算过程对大小写敏感。
    3、运算公式中仅允许小括号的出现,并且允许小括号的嵌套使用。
    4、如果要结束程序,请使用end命令。命令对大小写不敏感。
    5、程序正处于测试阶段,希望使用者能提供漏洞测试数据。
    6、表达式输入过程中不允许使用任何的不必要的空格,且每次仅仅允许计算一个表达式
    7、尽管程序理论上可以输入2*26个不同的命题变元进行运算,但由于其结果指数增长的特性,请不要随意尝试。否则后果自负。

    2、源代码:(这里将基础功能暂时封装成类)

      1 #include<cstring>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<ctype.h>
      5 #include<string>
      6 #include<algorithm>
      7 using namespace std;
      8 
      9 class LogicalExpression
     10 {
     11 private:
     12     int maxn;
     13     const char optr[6]="!*/:=";//运算符
     14     int *lch=nullptr;
     15     int *rch=nullptr;
     16     char *op=nullptr;
     17     int nc=0;//结点个数
     18     int MaxnEstimate(char* AimString);
     19     int Build_Tree(char* s,int x,int y);
     20     int GetValue(char* p,int* v,int root);
     21 public:
     22     LogicalExpression(){};
     23     ~LogicalExpression(){};
     24     void SetMaxn(char* AimString);
     25     void BuildLogicalExpressionTree(char* EX);
     26     void PresentTree();
     27     void ValueChart();
     28     void Clear();
     29 };
     30 
     31 int LogicalExpression::GetValue(char* p,int* v,int root)
     32 {
     33     char ch=op[root];
     34     //printf("%c
    ",ch);
     35     switch(ch)
     36     {
     37         case '!':return 1-GetValue(p,v,rch[root]);
     38         case '*':return (GetValue(p,v,lch[root])&&GetValue(p,v,rch[root]))?1:0;
     39         case '/':return (GetValue(p,v,lch[root])||GetValue(p,v,rch[root]))?1:0;
     40         case ':':return (GetValue(p,v,lch[root])==1&&GetValue(p,v,rch[root])==0)?0:1;
     41         case '=':return (GetValue(p,v,lch[root])==GetValue(p,v,rch[root]))?1:0;
     42     }
     43     //运行到此处说明是到达叶子
     44     //printf("->%d
    ",v[strchr(p,ch)-p]);
     45     return v[strchr(p,ch)-p];
     46 }
     47 
     48 void LogicalExpression::ValueChart()
     49 {
     50     //统计命题个数
     51     char temp[nc];
     52     memset(temp,0,sizeof(temp));
     53     int u=0;
     54     for(int i=1;i<=nc;i++)
     55     {
     56         char ch=op[i];
     57         if(strchr(temp,ch)==NULL&&strchr(optr,ch)==NULL)temp[u++]=ch;
     58     }
     59     sort(temp,temp+u);
     60     for(int t=0;t<u;t++)printf("%3c",temp[t]);
     61     printf(" ANS
    ");
     62     //创建真值分配数组
     63     int value[u];
     64     memset(value,0,sizeof(value));
     65     //i=[0,(1<<n)-1]循环
     66     for(int i=0;i<(1<<u);i++)
     67     {
     68         int c=i;
     69         for(int k=u-1;k>=0;k--)
     70         {
     71             value[k]=c%2;
     72             c/=2;
     73         }
     74         //分解i为二进制&&分配到各个命题
     75         int ans=GetValue(temp,value,1);
     76         for(int t=0;t<u;t++)printf("%3d",value[t]);
     77         printf("%3d
    ",ans);
     78     }
     79     //
     80     return;
     81 }
     82 
     83 void LogicalExpression::BuildLogicalExpressionTree(char* EX)
     84 {
     85     memset(lch,0,sizeof(lch));
     86     memset(rch,0,sizeof(rch));
     87     memset(op,0,sizeof(op));
     88     int lenth=strlen(EX);
     89     Build_Tree(EX,0,lenth);
     90     return;
     91 }
     92 
     93 int LogicalExpression::MaxnEstimate(char* AimString)
     94 {
     95     int lenth=strlen(AimString);
     96     int num=0;
     97     for(int i=0;i<lenth;i++)
     98     if(AimString[i]=='('||AimString[i]==')')num++;
     99     return lenth-num+1;
    100 }
    101 
    102 void LogicalExpression::SetMaxn(char* AimString)
    103 {
    104     maxn=MaxnEstimate(AimString);
    105     lch = new int[maxn+1];
    106     rch = new int[maxn+1];
    107     op  = new char[maxn+1];
    108     nc=0;
    109     return;
    110 }
    111 
    112 void LogicalExpression::Clear()
    113 {
    114     if(lch!=nullptr){delete[] lch;lch=nullptr;}
    115     if(rch!=nullptr){delete[] rch;rch=nullptr;}
    116     if(op !=nullptr){delete[] op ;op =nullptr;}
    117     maxn=nc=0; return;
    118 }
    119 
    120 int LogicalExpression::Build_Tree(char* s,int x,int y)
    121 {
    122     int c[]={-1,-1,-1,-1,-1};
    123     int u;
    124     if(y-x==1)//仅一个字符,建立单独结点
    125     {
    126         u=++nc;
    127         lch[u]=rch[u]=0;
    128         op[u]=s[x];
    129         return u;
    130     }
    131     //
    132     int p=0;
    133     for(int i=x;i<y;i++)//寻找括号外的运算符
    134     {
    135         if(s[i]=='(')p++;
    136         else if(s[i]==')')p--;
    137         else if(!p&&strchr(optr,s[i])!=NULL) c[strchr(optr,s[i])-optr]=i;
    138     }
    139     //
    140     int pst=-1;
    141     for(int k=4;k>=0;k--) if(c[k]!=-1) {pst=c[k];break;}
    142     //寻找在括号外优先级最低的二目运算符
    143     if(pst==-1) return Build_Tree(s,x+1,y-1);
    144     //整个式子都被包裹在括号里
    145     u=++nc;
    146     if(s[pst]=='!') lch[u]=-1;//右子树为-1说明是'!'运算
    147     else lch[u]=Build_Tree(s,x,pst);
    148     rch[u]=Build_Tree(s,pst+1,y);
    149     op[u]=s[pst];
    150     return u;
    151 }
    152 
    153 void LogicalExpression::PresentTree()
    154 {
    155     printf("VertexNum==%d
    --------
    ",nc);
    156     printf("NUM:");
    157     for(int i=1;i<nc+1;i++)printf("%3d",i);
    158     printf("
    VER:");
    159     for(int i=1;i<nc+1;i++)printf("%3c",op[i]);
    160     printf("
    LCH:");
    161     for(int i=1;i<nc+1;i++)printf("%3d",lch[i]);
    162     printf("
    RCH:");
    163     for(int i=1;i<nc+1;i++)printf("%3d",rch[i]);
    164     printf("
    "); return;
    165 }
    166 
    167 /************************************************************/
    168 
    169 int main()
    170 {
    171     //freopen("input.txt","r",stdin);
    172     //freopen("ans.txt","w",stdout);
    173     LogicalExpression psd;
    174     char aim[100];
    175     memset(aim,0,sizeof(aim));
    176     printf(">>");
    177     while(~scanf("%s",aim))
    178     {
    179         if(tolower(aim[0])=='e'&&tolower(aim[1])=='n'&&tolower(aim[2])=='d')break;
    180         psd.SetMaxn(aim);
    181         psd.BuildLogicalExpressionTree(aim);
    182         //psd.PresentTree();
    183         psd.ValueChart();
    184         psd.Clear();
    185         memset(aim,0,sizeof(aim));
    186         printf(">>");
    187     }
    188     return 0;
    189 }
    Line 190

    3、有一说一,这次的算法并不让我觉得很好。算法的思路是参考了一些资料确定下来的。

    首先是上述代码的参考源代码。由于之前只接触过一次类似的问题,所以这一次保留了一些前辈提供的模板。

     1 const int maxn = 1000 + 10; 
     2 char str[maxn];
     3 int lch[maxn + 1], rch[maxn + 1]; char op[maxn + 1];     //每个结点的左右子结点编号和字符
     4 int nc = 0;         //结点数 
     5 int build_tree(char* s, int x, int y) 
     6 {  
     7     int i, c1=-1, c2=-1, p=0;  
     8     int u; 
     9     if(y-x == 1)   //仅一个字符,建立单独结点 
    10     {        
    11         u = ++nc;    
    12         lch[u] = rch[u] = 0; 
    13         op[u] = s[x];   
    14         return u;
    15      } 
    16 
    17      for (i = x; i < y; i++)   //寻找根节点的位置
    18      {
    19          switch (s[i]) 
    20          {
    21             case '(':  p++; break;     
    22             case ')':  p--; break;      
    23             case '+':
    24             case '-':  if (!p) c1 = i; break;      
    25             case '*': 
    26             case '/':  if (!p) c2 = i; break;
    27          }
    28      }  
    29     if (c1 < 0) c1 = c2;     //找不到括号外的加减号,就用乘除号  
    30     if(c1 < 0) return build_tree(s, x+1, y-1);     //整个表达式被一对括号括起来  
    31     u = ++nc;  
    32     lch[u] = build_tree(s, x, c1); 
    33     rch[u] = build_tree(s, c1+1, y);  
    34     op[u] = s[c1];  
    35     return u;
    36 }

    上述这段代码在解决这种问题上十分容易理解。在修改这段代码时,需要时刻关注优先级和符号结合特征的问题。在四则运算中,符号的都是二目运算符且只有两个优先级。但是,在逻辑运算中却有两种结合特性的运算符以及五个不同的优先级。上述代码依旧借助了“编号化”的思想,使得在建立模拟二叉树的过程中能够提前估算二叉树规模。这在我封装好的类中有所体现。

    代码的来源:https://www.cnblogs.com/lfri/p/9985993.html

    第二个参考资料是一篇论文。《一种利用二叉树来实现逻辑表达式自动推导的算法》来自信阳师范学院学报第18卷第2期2005年4月(自然科学版)。

    论文对于这一部分的说法十分简洁,这里通过这次课题来进行补充。该程序之后的设计也会参考论文之后的讲解。但是由于采用的数据结构的差异,我不会完全按照论文的讲解去做。

    OK

  • 相关阅读:
    GridView, ListView 区别
    ActivityGroup和TabActiviy的差异性?
    Java加密解压
    Android代码中实现WAP方式联网
    SVN创建资源库和远程连接配置
    高仿优酷Android客户端图片左右滑动(自动切换)
    andoid 多线程断点下载
    Android中用Java代码实现zip文件解压缩
    JAVA两种实现二分查找方式
    三种JAVA编程方法实现斐波那契数列
  • 原文地址:https://www.cnblogs.com/savennist/p/12491911.html
Copyright © 2011-2022 走看看