数据结构学习笔记--二叉表达式树

 
终于到谈到树了,可以说数据结构最精彩的算法都出自这里 (但不是最复杂的,后面还有图..)。接下来的2篇文章会介绍有关树的一些操作和应用。
树的两个基本用途,可以用物质和精神来比喻。
一个用途是做为数据储存,储存具有树结构的数据——目录、族谱等等。为了在实际上是线性的储存载体上(内存、磁盘)储存非线性的树结构,必须有标志指示出树的结构。因此,只要能区分根和子树,树可以采取各种方法来储存——多叉链表、左孩子-右兄弟二叉链表、广义表、多维数组。由于操作的需求,储存方法并不是随意选取的。比如,在并查集的实现上,选取的是双亲链表。
一个用途是做为逻辑判断,此时会常常听到一个名词——判定树。最常用的结构是二叉树 (见下文),一个孩子代表true,一个孩子代表false。关于多叉判定树,有个例子是求8皇后的全部解——这个连高斯都算错了(一共是92组解,高斯最开始说76组解),我们比不上高斯,但是我们会让computer代劳。
就像哲学界到现在还纠缠于物质和精神本源问题,实际上在树这里也是如此。有些情况下,并不能区分是做为储存来用还是做为判断来用,比如搜索树,既储存了数据,还蕴涵着判断,这在后续的文章会提到。
和后面的图相比,树更基本,也更常用。你可以不知道最短路径怎么求,却每时每刻都在和树打交道——看看你电脑里的文件夹吧 ^_^
首先要介绍的是树形结构中最基础、最常用的所谓二叉树。二叉树可以说是人们假想的一个模型,因此,允许有空的二叉树是无争议的。二叉树是有序的,左边有一个孩子和右边有一个的是不同的两棵二叉树树。做这个规定,是因为人们赋予了左孩子和右孩子不同的意义。二叉树有多种存储方式,下面只讲解链式结构。看各种讲数据结构的书,你会发现一个有趣的现象:在二叉树这里,基本操作有计算树高、寻找节点以及各种遍历,但就是没有插入、删除操作。那树是怎么建立起来的?其实这很好理解,对于非线性的树形结构,插入删除操作不在一定的法则规定下,是毫无意义的。因此,只有在具体的应用中,才会有插入删除操作。
与之前的文章一样,这里不讨论各种基本操作,而是讲一些实际应用。我发现这样更有利于加深对数据结构的理解。这里讲书上的例子:二叉表达式树。但书上一个具体算法都没有给出,倒是随书光盘里给出了一个中缀表达式建树的演示,我就根据它编写了一个完整的表达式树类,下面以它来介绍二叉树的一些典型操作。包括:输入任意形式的表达式建立一棵树,表达式树的求值以及通过不同方式的遍历来生成不同形式的表达式。
首先是结点结构。与前面学过的线形链表一样,二叉树的结点也分为数据域与指针域,只不过这里有两个指针域分别指向左右孩子节点。有时为了操作方便还会附加一个双亲指针,这里就不需要了,下面是节点定义:
 
class  TNode {
public :
    union { 
char  optr;  int  opnd; };
    TNode
*  left;
    TNode
*  right;
    TNode(
char  op, TNode *  lef, TNode *  rgt)
        : optr(op), left(lef), right(rgt) {}
    TNode(
int  num)
        : opnd(num), left(NULL), right(NULL) {}
};
 
在表达式中又分运算数与操作符,所以结点的数据域必须设成联合的形式。同时为了便于生成新结点而加了两个构造函数。因为这个表达式类只是为了介绍二叉树,所以为了简便起见规定操作数只能是 1~9的整数。
表达式类的定义很简单,只需要存储一个指向树根的指针就够了,并且不需要做任何的初始化操作。下面是类的定义及部分函数声明。
 
#include  " ../../线性表(链式存储)/栈/Stack.h "
#include 
" ../../require.h "
#include 
< iostream >
#include 
< string >
class  ExpTree {
    TNode
*  m_pRoot;
public :
    ExpTree() : m_pRoot(NULL) {}
    
~ ExpTree() { destroy(m_pRoot); }
    
// 求值
     int  value() {  return  val(m_pRoot); }
private :
    
// 销毁树
     void  destroy(TNode *  cur) { 
        
if  (cur) { 
            destroy(cur
-> left); 
            destroy(cur
-> right); 
            delete cur; 
        } 
    }
    
// 计算
     int  cacul( int  a,  char  op,  int  b) {
        
switch  (op) {
        
case   ' + ' return  a  +  b;
        
case   ' - ' return  a  -  b;
        
case   ' * ' return  a  *  b;
        
case   ' / ' return  a  /  b;    
        }
    }
    
// 求值函数
     int  val(TNode *  cur) {
        
if  (cur -> left  ==  NULL  &&  cur -> right  ==  NULL)
            
return  cur -> opnd;
        
else
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值