重言式的判别
来源:互联网 发布:淘宝商城家具2门大橱 编辑:程序博客网 时间:2024/04/29 03:49
小编今年大二,数据结构让搞个重言式的判别,感觉写得很成功,传上来给大家分享。
#include<stdio.h>#include<math.h>#define LENGTHOFDATA 50 //输入数据预计的长度#define STACK_INIT_SIZE 100 //定义栈的初始长度#define STACKINCREMENT 10 //当栈长度不足时的增量char string[LENGTHOFDATA];int permutation[LENGTHOFDATA];typedef struct //栈的结构体 {char * base; //栈的底部指针char * top; //栈的头指针int stacksize; //栈的长度}SqStack;int InitStack(SqStack * s) //栈的初始化{s->base = malloc(STACK_INIT_SIZE * sizeof(char)); //为栈分配初始长度if (!s->base) //若失败就退出{exit(1);}s->top = s->base;s->stacksize = STACK_INIT_SIZE;return 1;}char GetTop(SqStack * s) //获得栈顶元素{char e;if (s->top == s->base){exit(1); //若栈为空,则退出}e = *(s->top - 1);return e;}void Push(SqStack * s, char e) //进栈{if (s->top - s->base >= s->stacksize) //若空间不足,则增加栈的空间{s->base = realloc(s->base, (s->stacksize + STACKINCREMENT) * sizeof(char));if (!s->base){exit(1);}s->top = s->base + s->stacksize;s->stacksize += STACKINCREMENT;}*s->top = e;s->top++;}char Pop(SqStack *s) //出栈{char e;if (s->base == s->top){exit(1); //若栈为空,则退出}s->top--;e = *s->top;return e;}char Precede(char a, char b) //逻辑运算符之间的优先关系判断函数 {int i, j;char Relationship[6][7] = //把优先关系表做成二维数组,从中寻找优先关系 {' ', '|', '&', '~', '(', ')', '#','|', '>', '<', '<', '<', '>', '>','&', '>', '>', '<', '<', '>', '>','~', '>', '>', '>', '<', '>', '>','(', '<', '<', '<', '<', '=', ' ','#', '<', '<', '<', '<', ' ', '='};for (i = 1; i < 6; i++){if (Relationship[i][0] == a){break;}}for (j = 1; j < 7; j++){if (Relationship[0][j] == b){break;}}return Relationship[i][j];}int Operate(int a, char theta, int b){switch (theta){case '&':if (a == 1 && b == 1)return 1;return 0;break;case'|':if (a == 1 || b == 1)return 1;return 0;break;case'~':if (a == 0)return 1;return 0;break;}}int EvaluateExpression(){char Precede(char a, char b);int indexOfString = 0;SqStack optr; //存放逻辑运算符的栈SqStack opnd; //存放大写字母的栈char c; //接收输入事件每一种可能的取值char x; //在c为等号时,接收出栈的符号char theta; //在c为大于号时,接收出栈的符号char a; //a和b分别为theta两旁的操作数char b;char temp; //临时存放operte函数的结果InitStack(&optr);Push(&optr, '#');//初始情况时先向存放逻辑运算符的栈中压入‘#’表示式子的开端,最后加一个‘#’表示结尾。c = string[indexOfString++];InitStack(&opnd);while (c != '#' || GetTop(&optr) != '#'){if (c >= 65 && c <= 90) //判断是否大写字母{c = permutation[c - 65] + 48;Push(&opnd, c);c = string[indexOfString++];}else //否则为运算符{switch (Precede(GetTop(&optr), c)){case '<':Push(&optr, c);c = string[indexOfString++];break;case '=':x = Pop(&optr);c = string[indexOfString++];break;case '>':theta = Pop(&optr);if (theta != '~'){b = Pop(&opnd) - 48;}else{b = 48;}a = Pop(&opnd) - 48;temp = Operate(a, theta, b) + 48;Push(&opnd, temp);break;}}}return GetTop(&opnd) - 48;}int getKind(char string[]) //获取测试数据不同事件的个数{int letter[26] = { 0 };int i;int sum = 0;for (i = 0; string[i] != '#'; i++){if (string[i] < 65 || string[i] > 90){continue;}letter[string[i] - 65] = 1;}for (i = 0; i < 26; i++){if (letter[i] == 1){sum++;}}return sum;}typedef struct tree //定义二叉树节点的结构体{int letter;int or; //用于判别在全排列中是否经过该节点struct tree *right, *left; //该节点的左右孩子struct tree *par; //该节点的父节点}tree;tree root;void addTree(tree * p, int length) //根据给予的二叉树的深度在根节点的基础上添加树节点{if (length > 0){tree *t1, *t2;t1 = malloc(sizeof(tree));t2 = malloc(sizeof(tree));t1->letter = 0;t2->letter = 1;t1->or = t2->or = 0;t1->par = p;t2->par = p;p->left = t1;p->right = t2;addTree(p->left, length - 1);addTree(p->right, length - 1);}}void createTree(int l) //建立二叉树{int result; //存放每次EvaluateExpression函数运行的结果int resultCount[50]; //将每次EvaluateExpression函数运行的结果收集在一起,以判别是否为重言式int indexOfresultCount = 0; //resultCount的下标,用于遍历int i = 0;int length = l; //l为二叉树的层数int sum; //存放一共有多少测试数据int temp; //在全排列被个事件的发生情况时临时存放长度int index; //存放可能结果数组num的下标tree *p; //addTree函数中用于构建二叉树的临时指针tree *t; //在全排列被个事件的发生情况时临时指针p = &root;root.letter = 0;root.or = 0;addTree(&root, length);temp = length;sum = pow(2, length);while (sum--){t = &root;temp = length;index = 0;while (temp--){if (t->left->or == 0){t = t->left;permutation[index++] = t->letter;}else{t = t->right;permutation[index++] = t->letter;}}t->or = 1;while (t = t->par){if (t->left->or == 1 && t->right->or == 1){t->or = 1;}else{break;}}result = EvaluateExpression();resultCount[indexOfresultCount++] = result;}temp = indexOfresultCount; //判断是否为重言式for (i = 0; i < temp; i++){if (resultCount[i] == 1)//检测到一个数据测试成立,就将resultCount的总数-1。indexOfresultCount--;}if (indexOfresultCount == 0)//resultCount数为0,说明对于所有的测试数据都成立,即为重言式{printf("True forever");}else if (indexOfresultCount == temp){printf("False forever");}else{printf("Satisfactible");}}int main(){int count; //测试数据的个数int i;char c;int kind; //每一次测试数据不同事件的个数printf("请输入共有几组测试数据:");scanf("%d", &count);c = getchar();//先接受上面scanf函数输入时按下的回车键while (count--){for (i = 0; c = getchar(); i++){if (c == 10){string[i] = '#';break;}string[i] = c;}kind = getKind(string);createTree(kind);printf("\n");}return 0;}
下面是程序设计书
可能看一下比较好理解
一、需求分析
1.一个逻辑表达式如果对于其变元的任一种取值均为真,则成为重言式;反之,如果对于其变元的任一种取值都为假,则称为矛盾式,然而,更多的情况下,既非重言式,也非矛盾式。写一个程序通过真值表判别一个逻辑表达式属于上述哪一类。基本要求如下:
(1)逻辑表达式从终端输入,长度不超过一行。逻辑运算符包括“|”、“&”和“~”,分别表示或、与和非,运算优先程度递增,但可有括号改变,即括号内的运算优先。逻辑变元为大写字母。表达式中任何地方都可以含有多个空格符。
(2)若是重言式或矛盾式,可以只显示“True Forever”或“False Forever”,否则显示“Statisfactible”以及变量名序列,与用户交互。若用户对表达式变元取定一组值,程序就求出并显示逻辑表达式的值。
(3)本程序先使用栈将逻辑表达式的变量进行存储,然后将栈中的元素作为二叉树的结点结构,然后根据优先级读取表达式建立二叉树,并通过逐个判断根实现对重言式的判别。
2. 程序执行的命令
(1)输入逻辑表达式 (2)判断表达式是重言式还是矛盾式 (3)若既不是重言式也不式矛盾式,则对变元取定值,并显示逻辑表达式的值 (4)结束
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;
[预计输出结果]
True Forever
False Forever
True Forever
False Forever
Statisfactible
Statisfactible
二. 概要设计
1.总体的流程:在main函数中输入式子,首先对式子中不同时间的个数n进行判别。再根据n来建立一个叶子节点为n的满二叉树,便于用来记录对于各种不同事件可能取值的全排列。再把这些可能取到的数据一次进行检测。从而达到判别重言式的目的。
2. 为实现上述程序功能,需要两个抽象数据类型,如下:
(1)二叉树模型
ADT tree
{数据对象D:D={ai | ai∈ElemSet, i=1,2,…,n, n≥0}
数据关系R:若D为空集,则称为空二叉树。
若D仅含一个数据元素,则R为空集,否则R={H},H满足关系:
(1) T中存在唯一的一个结点,它没有前驱,称为树的根,用root表示,在集合D中用a1表示;
(2) 若D中元素个数大于1,对于任意的数据元素aj∈D且j≥2,存在唯一的数据元素ai∈D,有<ai, aj>∈H;
(3) 若D中元素个数大于1,对于任意的数据元素ai∈D,仅存在不多于2个数据元素aj,ak∈D且j, k≥i,有< ai, aj >, < ai, ak >∈H,其中,若j<k,则称aj为ai的左孩子节点,ak为ai的右孩子节点。
(4) 对于任意的数据元素aj∈D且j≥2,存在D1 D,D1={ | ∈ElemSet, k=1,2, …,m, m≥0},有唯一的Pj={< , >, < , >, …, < , >, < , >},这个集合Pj表示从根结点到结点aj的一条路径。
基本操作P:
void createTree(int l)
操作结果:构造二叉树。
void addTree(tree * p, intlength)
前提条件:二叉树已建立。
操作结果:根据形参length来对p进行添加树节点,构造一个深度为length的二叉树。
}tree
(2)识别表达式使用的栈定义
ADT SqStack
{数据对象:D={ai| aiÎElemSet,i=1,2,...,n, n> 0}
数据关系:R1={ <>ai-1 ,ai>|ai-1, aiÎD, i=1,2,...,n }
约定an端为栈顶,ai端为栈底。
基本操作P:
int InitStack(SqStack * s)
操作结果:构造一个栈。
char GetTop(SqStack * s)
前提条件:栈已经存在。
操作结果:返回栈顶元素。
void Push(SqStack * s, chare)
前提条件:栈已经存在
操作结果:将元素e压入栈中。
char Pop(SqStack *s)
前提条件:栈已经存在,且栈非空。
操作结果:将栈顶元素充当返回值返回。
} ADT SqStack
3.本程序主要分为四个模块
(1)主函数模块
输入共有几组测试数据,输入每组测试数据,调用各种函数进行测试。
(2)判断数据不同事件个数模块
对于每组测试数据,计算出不同时间的个数,充当返回值返回。
(3)二叉树的建立模块
对与每组不同的测试数据以及测试数据的不同事件的个数。求出该测试数据的对于每个不同事件可能取值的全排列。全排列数据存放在二叉树组中。对于测试后的结果,进行最后的判别。
(4)测试数据模块
把每次的测试数据的全排列传递给该模块,该模块利用栈的性质,来进行测试,判断是为真还是为假。
4.程序各个模块间的关系
三. 详细设计
1.栈类型和二叉树类型
#defineSTACK_INIT_SIZE 100 //定义栈的初始长度
#defineSTACKINCREMENT 10 //当栈长度不足时的增量
typedefstruct //栈的结构体
{
char * base; //栈的底部指针
char * top; //栈的头指针
int stacksize; //栈的长度
}SqStack;
intInitStack(SqStack * s) //栈的初始化
{
s->base = malloc(STACK_INIT_SIZE *sizeof(char)); //为栈分配初始长度
if (!s->base) //若失败就退出
{
exit(1);
}
s->top = s->base;
s->stacksize = STACK_INIT_SIZE;
return 1;
}
charGetTop(SqStack * s) //获得栈顶元素
{
char e;
if (s->top == s->base)
{
exit(1); //若栈为空,则退出
}
e = *(s->top - 1);
return e;
}
void Push(SqStack* s, char e) //进栈
{
if (s->top - s->base >=s->stacksize) //若空间不足,则增加栈的空间
{
s->base = realloc(s->base,(s->stacksize + STACKINCREMENT) * sizeof(char));
if (!s->base)
{
exit(1);
}
s->top = s->base + s->stacksize;
s->stacksize += STACKINCREMENT;
}
*s->top = e;
s->top++;
}
char Pop(SqStack*s) //出栈
{
char e;
if (s->base == s->top)
{
exit(1); //若栈为空,则退出
}
s->top--;
e = *s->top;
return e;
}
//对于main函数中输入的式子,通过getKind函数得到不同事件的个数n,由于每个事件只有两种情况:真或假,我们用0和1来表示。这就是tree结构体重的letter,它用来表示该事件是否为真。
typedef structtree //定义二叉树节点的结构体
{
int letter;
int or; //用于判别在全排列中是否经过该节点
struct tree *right, *left; //该节点的左右孩子
struct tree *par; //该节点的父节点
}tree;
tree root;
void addTree(tree* p, int length) //根据给予的二叉树的深度在根节点的基础上添加树节点
{
if (length > 0)
{
tree *t1, *t2;//t1,t2分别为当前节点的左右子树。
t1 = malloc(sizeof(tree));//为子树申请空间
t2 = malloc(sizeof(tree));
t1->letter = 0;//对于每一个事件,都有两种情况0或1,所以有两个子节点,表示该事件为0和为1的两种情况,从而达到全排列。
t2->letter = 1;
t1->or = t2->or = 0;
//在后续的全排列中,用or来记录该节点是否测试过。
t1->par = p;
//建立父子关系。
t2->par = p;
p->left = t1;
p->right = t2;
addTree(p->left, length - 1);
addTree(p->right, length - 1);
//递归遍历。
}
}
voidcreateTree(int l) //建立二叉树
{
int result; //存放每次EvaluateExpression函数运行的结果
int resultCount[50]; //将每次EvaluateExpression函数运行的结果收集在一起,以判别是否为重言式
int indexOfresultCount = 0; //resultCount的下标,用于遍历
int i = 0;
int length = l; //l为二叉树的层数
int sum; //存放一共有多少测试数据
int temp; //在全排列被个事件的发生情况时临时存放长度
int index; //存放可能结果数组num的下标
tree *p; //addTree函数中用于构建二叉树的临时指针
tree *t; //在全排列被个事件的发生情况时临时指针
p = &root;
root.letter = 0;
root.or = 0;
addTree(&root, length);
temp = length;
sum = pow(2, length);
//把每种情况输入到EvaluateExpression函数中去。
while (sum--)
{
t = &root;
temp = length;
index = 0;
while (temp--)
{
if (t->left->or == 0)
{
t = t->left;
permutation[index++] = t->letter;//permutation数组用来记录每次全排列的可能情况,为全局变量。
}
else
{
t = t->right;
permutation[index++] = t->letter;
}
t->or = 1;
//如果该节点已经1测试了,将它的or赋1.
//检验,从叶子节点开始,如果左右孩子的or为1,则将父节点的or也设为1.
while (t = t->par)
{
if (t->left->or == 1 &&t->right->or == 1)
{
t->or = 1;
}
else
{
break;
}
//将这种结果给予到EvaluateExpression函数中去。
result = EvaluateExpression();
//将每次的测试结果放入数组中,以便于下面重言式的最后判别。
resultCount[indexOfresultCount++] = result;
}
temp = indexOfresultCount; //判断是否为重言式
for (i = 0; i < temp; i++)
{
if (resultCount[i] == 1)//检测到一个数据测试成立,就将resultCount的总数-1。
indexOfresultCount--;
}
if (indexOfresultCount == 0)//resultCount数为0,说明对于所有的测试数据都成立,即为重言式
{
printf("True forever");
}
else if (indexOfresultCount == temp)
{
printf("False forever");
}
else
{
printf("Satisfactible");
}
}
2.测试模块
int Operate(inta, char theta, int b)
{//对传过来的两个操作数a和b以及操作符进行运算
switch (theta)//判断运算符的种类,进行相应的计算
{
case '&':if (a == 1 && b == 1)
return 1;
return 0;
break;
case'|':if (a == 1 || b == 1)
return 1;
return 0;
break;
case'~':if (a == 0)
return 1;
return 0;
break;
}
}
//对每个式子的每一个不同全排列进行测试
int EvaluateExpression()
{
char Precede(char a, char b);
int indexOfString = 0;
SqStack optr; //存放逻辑运算符的栈
SqStack opnd; //存放大写字母的栈
char c; //接收输入事件每一种可能的取值
char x; //在c为等号时,接收出栈的符号
char theta; //在c为大于号时,接收出栈的符号
char a; //a和b分别为theta两旁的操作数
char b;
char temp; //临时存放operte函数的结果
InitStack(&optr);
Push(&optr, '#');//初始情况时先向存放逻辑运算符的栈中压入‘#’表示式子的开端,最后加一个‘#’表示结尾。
c = string[indexOfString++];//取得式子中的操作数或操作符
InitStack(&opnd);
while (c != '#' || GetTop(&optr) !='#')
{
if (c >= 65 && c <= 90) //判断是否大写字母,即是否为操作数
{
c = permutation[c - 65] + 48;//把该事件在permutation数组中对应的取值提取出来,压入栈中。由于permutation是int数组,需要+48.
Push(&opnd, c);
c = string[indexOfString++];//取下一个符号
}
else //否则为运算符
{
switch (Precede(GetTop(&optr), c))//根据Precede函数来判断运算符之间的优先关系。
{
case '<':
Push(&optr, c);
c = string[indexOfString++];
break;
case '=':
x = Pop(&optr);
c = string[indexOfString++];
break;
case '>':
theta = Pop(&optr);
//这里有一个特殊情况需要考虑,&和|都是进行两个操作数的运算,而~只需要一个操作数。
if (theta != '~')
{
b = Pop(&opnd) - 48;
}
else
{
b = 48;
}
a = Pop(&opnd) - 48;//int和char数据之间的转换
temp = Operate(a, theta, b) + 48;
Push(&opnd, temp);
break;
}
}
}
return GetTop(&opnd) - 48;//将判断结果返回
}
int getKind(charstring[]) //获取测试数据不同事件的个数
{
int letter[26] = { 0 };建立一个存放26个数字的数组,由于统计式子中不同事件的个数
int i;
int sum = 0;
for (i = 0; string[i] != '#'; i++)
{
if (string[i] < 65 || string[i] > 90)//判断是否为事件(事件都用大写字母表示)
{
continue;
}
letter[string[i] - 65] = 1;//在对应的letter数组的下表,使其对应的值为1.
}
for (i = 0; i < 26; i++)//统计不同时间的个数。
{
if (letter[i] == 1)
{
sum++;
}
}
return sum;
}
3.主函数模块
int main()
{
int count; //测试数据的个数
int i;
char c;
int kind; //每一次测试数据不同事件的个数
printf("请输入共有几组测试数据:");
scanf("%d", &count);
c = getchar();//先接受上面scanf函数输入时按下的回车键
while (count--)
{
for (i = 0; c = getchar(); i++)
{
if (c == 10)
{
string[i] = '#';//如果用户输入的数据已经结束,则在最后加上#符号,为了在测试模块中进行测试。
break;
}
string[i] = c;
}
kind = getKind(string);
createTree(kind);
printf("\n");
}
return 0;
}
四.测试结果
输入一共有3组测试数据
键入逻辑表达式(A|~A)&(B|~B)
判断出该逻辑表达式是重言式
输入逻辑表达式(A&~A)&C
判断出该逻辑表达式是矛盾式
键入逻辑表达式(A|B)&(A|~B)
判断出该逻辑表达式既不是重言式也不是矛盾式
- 重言式的判别
- 重言式判别
- 重言式判别 (数据结构课程设计)
- C++重言式判别
- 数据结构实习——重言式的判别(写的不好不要见怪)
- 重言式判定------参考了别人的代码。。
- 闰年的判别
- 运算放大器好坏的判别
- 同构树的判别
- 从句的判别
- 闰年的判别.
- 三极管PNP NPN 的判别
- 判别ios设备的类型
- 运算放大器的好坏判别方法
- 判别ios设备的类型
- 判别ios设备的类型
- 三极管的引脚判别方法
- 判别系数的通俗理解
- EditText获取焦点
- 关于使用Robotium进行Android测试的一点小改进
- linux 下 OpenGL 读取 JPG, PNG, TAG 纹理数据
- mysql中find_in_set函数的应用
- ubunto 中安装adb
- 重言式的判别
- 深入理解JVM性能调优
- iOS开发的22个奇谲巧技
- 今天尝试打包自己的类库
- 同步NTP 服务器时间错误:Updating Time : ntpdate[3108]: the NTP socket is in use, exiting
- 小明,你滚出去。。。
- jetty9.2.6 安装 部署 运行
- Java多线程编程(六)-并发编程原理(Java存储模型和共享对象)
- #2002 无法登录 MySQL 服务器(害死人的3306端口被占用)