初步了解“树”

初步了解“树”
开发工具与关键技术:Visual Studio 2015 初步了解“树”
作者:李国旭
撰写时间:2020年5月15日
什么是“树”?我们就疑问了。它并不是指路边的那些树木,只是它的的长相像一颗树而已。比如说一个家庭有4名成员:张三有俩个儿子,大儿子张青,次子张虹,大儿子张青的儿子叫张寻,这个家庭父子之间的关系就可以用树型结构表示,如下图表示。
初步了解“树”_第1张图片
树型结构的数据元素之间呈现分支、分层的特点。树型结构在我们的日常生活中都可以见到的:一个家族、公司、组织等等…都可以用我们的树来表示。还有就是我们的操作系统中的目录树、数据库中信息的组织,下面就是一幅省、市、区的树形图结构图。还有我们电脑上面经常看到的文件目录,根目录里面又有很多子目录和文件,子目录下再包含子目录和文件,这些都是很常见的树关系。
初步了解“树”_第2张图片
首先,我们先了解一下关于树的概念:树是由n(n>=0)个结点构成的有限集合T,当n=0时T称为空树;否则,在任一非空树T中;(1)有且仅有一个特定的特点,它没有前驱结点,称其为根结点;(2)剩下的结点可分为m(m>=0)个互不相交的子集T1,T2,…,Tm,其中每个子集本身又是一棵树,并称其为根的子树。(注意:树的定义具有递归性,即“树中还有树”。树的递归定义揭示出了树的固有特性)下图来自朱亚兴老师的
初步了解“树”_第3张图片
而且在我们的计算机中,就只有顺序存储和链式存储,它的表示方法有很多种;我们就来看看树的三种主要的表示方法(a双亲表示法、b孩子表示法、c孩子兄弟表示法)而树呢!是一个(非线性结构)。
(1)双亲(数组)表示法是一种顺序储存结构,这种表示法用一维数组来存储树的有关信息,它会将树中的结点从上到下,从左到右的顺序存放在一个一维数组中,每个数组元素中存放一个节点信息。树的双亲表示法的定义如下:

//树的双亲表示法节点结构定义
#define MAX_TREE_SIZE 100
typedef int ElemType;
typedef struct
{
    ElemType data;  //节点位置
    int parent;     //双亲位置
}PTNode;
typedef struct
{
    PTNode nodes[MAX_TREE_SIZE];
    int r;  //根的位置
    int n;  //节点数目
}PTree;

但是,对于该种表示方法,已知孩子节点寻找双亲非常简单,时间复杂度为O(1),但是对于已知某双亲节点寻找某孩子节点,就需要遍历整个树结构。
当然可以对上述结构进行改进从而克服该缺点,就是对于每个孩子节点,再附设指示其孩子节点在数组中位置的元素,但是由于各个节点的孩子数不同,就需要根据节点度的最大值来决定需要附设几个指示元素,当然会造成空间的浪费,应了那句话“提高空间效率便会增加时间复杂度,提高时间效率便会增加空间复杂度。”这句话,对于研究算法就有着重要的一个因素,如何在解决某个问题的时候,实现效率和复杂度的最优化,找到最好的平衡点。
(2)孩子表示法:类比于上面的双亲表示法,该方法无非就是在每个节点附设存放孩子位置的指示。但是它就存在一个问题,也就是在上面也曾经提到过,就是每个节点的孩子的数目是不一定相同的,甚至差别较大,此时如果单纯按照节点度的最大值来声明足够的空间,则必然造成很大的浪费,因为有很多的指针域是空的。针对以上这个问题,此时我们可以想到另一种表示方法,就是在每个节点中存储节点的度,按照节点的度来申请空间,但是还是不行啊,这种结构不好统一处理,而且初始化和维护起来难度大,用这个树的孩子链表示法。(下图来自朱亚兴老师),如图所示:
初步了解“树”_第4张图片

#define MAX_TREE_SIZE
typedef char ElemType
//孩子节点
typedef struct CTNode
{int child;            //孩子节点下标
 struct CTNode *next;  //指向下一个孩子节点的指针
}*childPtr;
//表头节点
typedef struct
{   ElemType data;       //存放在树中的节点的数据
    int parent;          //存放双亲的下标
    childPtr firstChild; //指向第一个孩子的指针
}CTBox;
//树结构
typedef struct
{
    CTBox nodes[MAX_TREE_SIZE];  //节点数组
    int r;                       //根的位置
    int t;                       //节点数目
}CTree;

(3)孩子兄弟表示法:上面两种方法分别从孩子的角度来表示树的存储结构,可以很快的找到结点的双亲或孩子,但是呢找结点的兄弟就会比较困难。任意一棵树的结点,如果他的第一个孩子和它的右兄弟如果存在,必定是唯一的,因此我们就可以设置两个指针,分别是指向孩子和此节点的兄弟data是数据域,firstchild是储存该节点第一个孩子结点的存储地址,rightsibling是储存该节点的右兄弟结点的存储地址。如下表格所示:
data firstchild rightsibling
这种孩子兄弟表示法方便了查找结点的孩子和兄弟,缺点是破坏了树的层次

#define MAX_TREE_SIZE
typedef char ElemType
//定义节点
typedef struct CTNode
{   ElemType data;       //存放在树中的节点的数据
    CTNode *firstChild;  //存放子节点
    CTNode *nextSibling; //存放其子节点的兄弟节点
}CTNode,*CTree;

你可能感兴趣的:(数据结构与算法)