数据结构实践——用二叉树求解代数表达式

简介: 本文是针对数据结构基础系列(6):树和二叉树的配套实践。【项目 - 用二叉树求解代数表达式】   用二叉树来表示代数表达式,树的每一个分支节点代表一个运算符,每一个叶子节点代表一个运算数(为简化,只支持二目运算的+、-、*、/,不加括号,运算数也只是一位的数字字符。本项目只考虑输入合乎以上规则的情况)。请设计算法,(1)根据形如“1+2∗3−4/5 1+2*3-4/5”

本文是针对数据结构基础系列(6):树和二叉树的配套实践。

【项目 - 用二叉树求解代数表达式】
  用二叉树来表示代数表达式,树的每一个分支节点代表一个运算符,每一个叶子节点代表一个运算数(为简化,只支持二目运算的+、-、*、/,不加括号,运算数也只是一位的数字字符。本项目只考虑输入合乎以上规则的情况)。请设计算法,(1)根据形如“ 1+234/5  ”的字符串代表的表达式,构造出对应的二叉树(如图),用后序遍历的思路计算表达式的值时,能体现出先乘除后加减的规则;(2)对构造出的二叉树,计算出表达式的值。
这里写图片描述

[参考解答] 程序中的btree.h,见二叉树算法库

#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include "btree.h"

//用s[i]到s[j]之间的字符串,构造二叉树的表示形式
BTNode *CRTree(char s[],int i,int j)
{
    BTNode *p;
    int k,plus=0,posi;
    if (i==j)    //i和j相同,意味着只有一个字符,构造的是一个叶子节点
    {
        p=(BTNode *)malloc(sizeof(BTNode));   //分配存储空间
        p->data=s[i];                         //值为s[i]
        p->lchild=NULL;
        p->rchild=NULL;
        return p;
    }
    //以下为i!=j的情况
    for (k=i; k<=j; k++)
        if (s[k]=='+' || s[k]=='-')
        {
            plus++;
            posi=k;              //最后一个+或-的位置
        }
    if (plus==0)                 //没有+或-的情况(因为若有+、-,前面必会执行plus++)
        for (k=i; k<=j; k++)
            if (s[k]=='*' || s[k]=='/')
            {
                plus++;
                posi=k;
            }
    //以上的处理考虑了优先将+、-放到二叉树较高的层次上
    //由于将来计算时,运用的是后序遍历的思路
    //处于较低层的乘除会优先运算
    //从而体现了“先乘除后加减”的运算法则
    //创建一个分支节点,用检测到的运算符作为节点值
    if (plus!=0)
    {
        p=(BTNode *)malloc(sizeof(BTNode));
        p->data=s[posi];                //节点值是s[posi]
        p->lchild=CRTree(s,i,posi-1);   //左子树由s[i]至s[posi-1]构成
        p->rchild=CRTree(s,posi+1,j);   //右子树由s[poso+1]到s[j]构成
        return p;
    }
    else       //若没有任何运算符,返回NULL
        return NULL;
}

double Comp(BTNode *b)
{
    double v1,v2;
    if (b==NULL)
        return 0;
    if (b->lchild==NULL && b->rchild==NULL)  //叶子节点,应该是一个数字字符(本项目未考虑非法表达式)
        return b->data-'0';    //叶子节点直接返回节点值,结点中保存的数字用的是字符形式,所以要-'0'
    v1=Comp(b->lchild); //先计算左子树
    v2=Comp(b->rchild); //再计算右子树
    switch(b->data)     //将左、右子树运算的结果再进行运算,运用的是后序遍历的思路
    {
    case '+':
        return v1+v2;
    case '-':
        return v1-v2;
    case '*':
        return v1*v2;
    case '/':
        if (v2!=0)
            return v1/v2;
        else
            abort();
    }
}

int main()
{
    BTNode *b;
    char s[MaxSize]="1+2*3-4/5";
    printf("代数表达式%s\n",s);
    b=CRTree(s,0,strlen(s)-1);
    printf("对应二叉树:");
    DispBTNode(b);
    printf("\n表达式的值:%g\n",Comp(b));
    DestroyBTNode(b);
    return 0;
}

目录
相关文章
|
6月前
|
存储 算法 Java
算法系列之数据结构-二叉树
树是一种重要的非线性数据结构,广泛应用于各种算法和应用中。本文介绍了树的基本概念、常见类型(如二叉树、满二叉树、完全二叉树、平衡二叉树、B树等)及其在Java中的实现。通过递归方法实现了二叉树的前序、中序、后序和层次遍历,并展示了具体的代码示例和运行结果。掌握树结构有助于提高编程能力,优化算法设计。
176 10
 算法系列之数据结构-二叉树
|
8月前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
331 77
|
8月前
|
存储 C++
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
【数据结构——树】哈夫曼树(头歌实践教学平台习题)【合集】目录 任务描述 相关知识 测试说明 我的通关代码: 测试结果:任务描述 本关任务:编写一个程序构建哈夫曼树和生成哈夫曼编码。 相关知识 为了完成本关任务,你需要掌握: 1.如何构建哈夫曼树, 2.如何生成哈夫曼编码。 测试说明 平台会对你编写的代码进行测试: 测试输入: 1192677541518462450242195190181174157138124123 (用户分别输入所列单词的频度) 预
187 14
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
|
8月前
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
179 12
|
8月前
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
160 10
|
8月前
|
算法 C++
【C++数据结构——图】最小生成树(头歌实践教学平台习题) 【合集】
【数据结构——图】最小生成树(头歌实践教学平台习题)目录 任务描述 相关知识 测试说明 我的通关代码: 测试结果:【合集】任务描述 本关任务:编写一个程序求图的最小生成树。相关知识 为了完成本关任务,你需要掌握:1.建立邻接矩阵,2.Prim算法。建立邻接矩阵 上述带权无向图对应的二维数组,根据它建立邻接矩阵,如图1建立下列邻接矩阵。注意:INF表示无穷大,表示整数:32767 intA[MAXV][MAXV];Prim算法 普里姆(Prim)算法是一种构造性算法,从候选边中挑
101 10
|
8月前
|
存储 算法 C++
【C++数据结构——图】图的邻接矩阵和邻接表的存储(头歌实践教学平台习题)【合集】
本任务要求编写程序实现图的邻接矩阵和邻接表的存储。需掌握带权有向图、图的邻接矩阵及邻接表的概念。邻接矩阵用于表示顶点间的连接关系,邻接表则通过链表结构存储图信息。测试输入为图的顶点数、边数及邻接矩阵,预期输出为Prim算法求解结果。通关代码提供了完整的C++实现,包括输入、构建和打印邻接矩阵与邻接表的功能。
261 10
|
8月前
|
C++
【C++数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】(1)遇到左括号:进栈Push()(2)遇到右括号:若栈顶元素为左括号,则出栈Pop();否则返回false。(3)当遍历表达式结束,且栈为空时,则返回true,否则返回false。本关任务:编写一个程序利用栈判断左、右圆括号是否配对。为了完成本关任务,你需要掌握:栈对括号的处理。(1)遇到左括号:进栈Push()开始你的任务吧,祝你成功!测试输入:(()))
183 7
|
10月前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
213 59
|
3月前
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
44 0
栈区的非法访问导致的死循环(x64)