C语言输入波兰式/逆波兰式创建表达式树,输出相应的中缀表达式和(逆)波兰式,并求值

1 基本要求

输入合法的波兰式(仅考虑运算符为双目运算符的情况),构建表达式树,分别输出对应的中缀表达式(可含有多余的括号)、逆波兰式和表达式的值,输入的运算符与操作数之间会用空格隔开。

2 选做要求(都已实现)

1)输出的中缀表达式中不含有多余的括号。
2)输入逆波兰式,输出波兰式、中缀表达式(可含有多余的括号)和表达式的值。

3 基本思路

波兰式、中缀表达式、逆波兰式即为表达式树的先序遍历结果、中序遍历结果、后序遍历结果。
将输入的表达式用字符串p进行存储,不同数字、运算符间用空格隔开。通过判断字符串的第一个字符是数字还是运算符,判断输入的是波兰式还是逆波兰式。
若输出波兰式,则自前至后处理字符串p,通过先序遍历创建表达式树,先创建左子树;若输入逆波兰式,则自后至前处理字符串p,仍通过先序遍历创建表达式树,但先创建右子树。
创建表达式的过程中进行求值,在结构体中定义value存储当前结点的值,即以当前结点为根的左右子树的运算结果。

4 代码
#include
#include
#include
#include

char p[50];//存储输入的表达式

typedef struct BiTNode{
    char data;
    int value;//存储以该节点为根的树的值
    struct BiTNode *lchild;//左孩子指针
    struct BiTNode *rchild;//右孩子指针
}BiTNode,*BiTree;

int PreCreateBiTree(BiTree T,int i){
    //输入波兰式时,先序创建表达式树
    //创建过程中计算表达式的值 
    //i 记录已处理的字符的位置 
    if(i >= strlen(p)) //字符串已处理完毕,停止递归 
        return i;
    if(p[i] == '+' || p[i] == '-' || p[i] == '*' || p[i] == '/'){
        T->data = p[i];
  	T->lchild = (BiTree)malloc(sizeof(BiTNode));
  	T->rchild = (BiTree)malloc(sizeof(BiTNode));
  	//因为输入数据间用空格隔开,所以要传递参数时i+2,可以跳过空格
  	int m = PreCreateBiTree(T->lchild,i+2);
  	int n = PreCreateBiTree(T->rchild,m+2);
  	switch(T->data){ //该结点的value等于其左右子树的运算结果 
   	    case '+' :T->value = T->lchild->value + T->rchild ->value;break;
   	    case '-' :T->value = T->lchild->value - T->rchild ->value;break;
   	    case '*' :T->value = T->lchild->value * T->rchild ->value;break;
   	    case '/' :T->value = T->lchild->value / T->rchild ->value;break;
        }
        return n;
    }
    else{
        T->data = '#';
        int j,num = 0;
        for(j = i;p[j] != ' '&&p[j] != '\0';j++){
            num = num*10 + (p[j]-'0');
  	}
  	T->value = num;
  	return j-1;
    }
}

int PostCreateBiTree(BiTree T,int i){
    //输入逆波兰式时,倒序处理字符串,先序创建表达式树
    //创建过程中计算表达式的值 
    //i 记录从后至前已处理的字符的位置 
    if(i < 0) 
        return i;
    if(p[i] == '+' || p[i] == '-' || p[i] == '*' || p[i] == '/'){
  	//倒叙处理字符串,故应先创建右子树 
  	T->lchild = (BiTree)malloc(sizeof(BiTNode));
  	T->rchild = (BiTree)malloc(sizeof(BiTNode));
  	int n = PostCreateBiTree(T->rchild,i-2);
  	int m = PostCreateBiTree(T->lchild,n-2);
  	T->data = p[i];
  	switch(T->data){
   	    case '+' :T->value = T->lchild->value + T->rchild ->value;break;
   	    case '-' :T->value = T->lchild->value - T->rchild ->value;break;
   	    case '*' :T->value = T->lchild->value * T->rchild ->value;break;
   	    case '/' :T->value = T->lchild->value / T->rchild ->value;break;
         }
         return m;
    }
    else{
        T->data = '#';
  	int j,num = 0;
  	for(j = i;p[j] != ' '&&j >= 0;j--){
   	    num = num + pow(10,i-j)*(p[j]-'0');
  	}
  	T->value = num;
  	return j+1;
    }
}

bool Low(char a,char b){
    //判断运算符啊a,b的优先级,若a的优先级低,返回true,否则,返回false 
    if((a == '+'||a == '-')&&(b == '*'||b == '/'))
  	return true;
    return false;
}

bool High(char a,char b){
    //判断运算符啊a,b的优先级,若a的优先级高,返回true,否则,返回false 
    if((a == '*'||a == '/')&&(b == '+'||b == '-'))
  	return true;
    return false;
}

void InBiTree(BiTree T){
    //输出中缀表达式
    if(T->data != '#'){
  	if(T->lchild){
   	    if(Low(T->lchild->data,T->data)){
    		printf("( ");
    		InBiTree(T->lchild);
    		printf(")");
   	    }
   	    else
    		InBiTree(T->lchild);
        }
        printf("%c ",T->data);
  	if(T->rchild){
  	    //为保证输出的中缀表达式中没有多余的括号,因此要分情况讨论
  	    //否则,对右子树的处理与对左子树相同
   	    if(High(T->rchild->data,T->data))
    		InBiTree(T->rchild);
   	    else if(T->rchild->data == '#')
    		InBiTree(T->rchild);
   	    else{
   	        //这种情况既包括右子树的优先级低的情况,也包括优先级相同的情况
   	        //相同优先级加括号是为了保证运算的顺序与表达式树表示的相同
    		printf("( ");
    		InBiTree(T->rchild);
    		printf(") ");
   	    }
        }
    }
    else{
  	printf("%d ",T->value);
    }
    return ;
}

void PreBiTree(BiTree T){
    //输出前缀表达式,即波兰式
    if(T->data != '#'){
  	printf("%c ",T->data);
  	PreBiTree(T->lchild);
  	PreBiTree(T->rchild);
    }
    else
  	printf("%d ",T->value);
    return ;
}

void PostBiTree(BiTree T){
    //输出后缀表达式,即逆波兰式
    if(T->data != '#'){
  	PostBiTree(T->lchild);
  	PostBiTree(T->rchild);
  	printf("%c ",T->data);
    }
    else
  	printf("%d ",T->value);
    return ;
}

int main(){
    BiTree root = (BiTree)malloc(sizeof(BiTNode));
    gets(p);
    if(p[0] >= '0' && p[0] <= '9'){
  	//输入表达式为逆波兰表达式
  	PostCreateBiTree(root,strlen(p)-1);
  	printf("前缀表达式: ");
  	PreBiTree(root);
  	printf("\n");
  	printf("中缀表达式: ");
  	InBiTree(root);
  	printf("\n");
  	printf("表达式的值: ");
  	printf("%d",root->value);
    }
    else{
  	//输入的为波兰式
  	PreCreateBiTree(root,0);
  	printf("中缀表达式: ");
  	InBiTree(root);
  	printf("\n");
  	printf("后缀表达式: "); 
  	PostBiTree(root);
  	printf("\n");
  	printf("表达式的值: "); 
  	printf("%d",root->value);
    }
    return 1;
}
5 调试分析

最初将存储表达式的字符串p作为参数传递给CreateBiTree函数,导致递归时出现了内存溢出的情况,将p改为全局变量后不再出现此类问题。

6 时间复杂度分析

表达式树本质也为二叉树,输出波兰式、中缀表达式、逆波兰式就是对二叉树的先序遍历、中序遍历和后序遍历,因此对其复杂度分析与对遍历二叉树的时间复杂度的分析是相同的。若输入表达式的长度为n(忽略空格),则创建表达式树、输出中缀表达式和波兰式/逆波兰式的时间复杂度都是O(n),由于创建过程中已经对表达式进行了求值,故求值过程无需额外的时间,因此程序总的时间复杂度为O(3n)。

7 思考讨论

表达式树是二叉树的一个应用,其先序序列、中序序列和后序序列分别对应了波兰式、中缀表达式和逆波兰式。若是给定波兰式或逆波兰式,可以唯一的确定一棵表达式树,因为式中已经包含了运算的优先级和运算顺序;而若是给定中缀表达式,在不考虑计算机的运算顺序时,其确定的表达式树是不唯一的,若考虑了计算机的运算顺序,其确定的表达式树也是唯一的。例如给定2 + 3 - 1,既可以将‘+’作为表达树的根,也可以将‘-’作为表达树的根,若结合计算机的运算顺序,则其表达式树的根为‘+’。
对比实验一的二叉树,我们可以发现二者有很大不同:由波兰式、中缀表达式、逆波兰式中的任意一种,都可以唯一确定一棵表达式树;而若想唯一确定一棵二叉树,则必须先知道序遍历、中序遍历、后序遍历中的任意两种。这是由于表达式树中数字必定为叶子结点,运算符必定不为叶子结点;而对一般二叉树却无法确定任意一结点是否为叶子结点。

你可能感兴趣的:(数据结构学习笔记)