学习算法和数据结构:树状结构

「学习算法和数据结构系列」之树状结构


树的基本概念

树是由n(n≥0)个结点的有限集;n=0时称为空树;在任意一棵非空树中:
1)有且仅有一个特定的称为根的结点;
2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1、T2……、Tm,其中每一个有限集本身又是一棵树,并切称为根的子树学习算法和数据结构:树状结构_第1张图片
树的定义用到了递归

结点分类

按照所处层次的不同,分为“根结点”、“分支结点”、“叶结点”

结点间的关系

按照所在的相对位置,有“子结点”、“父结点”、“兄弟结点”

结点的度

结点的度是指该结点的子结点个数

树的度

树的度是指树的各个结点的度的最大值

树的深度

从根结点开始为第一层,最大的层次数称为数的深度
文章开头的树的深度为4

树和森林

定义:森林是m(m≥0)棵互不相交的树的集合


树的ADT

Data
    树是由一个根结点和若干棵子树构成;树中结点具有相同的数据类型及层次关系
Operation
    InitTree(*T):构造空树
    DestroyTree(*T):销毁树T
    CreateTree(*T, definition):根据definition中给出的树的定义来构造树
    ClearTree(*T):若树存在,则将树T清空为空树
    TreeEmpty(T):若树为空树,返回treu,否则返回false
    Root(T):返回T的根结点
    Value(T, cur_e):cur_e是树T中的一个结点,返回此值
    Assign(T, cur_e, value):给树T的根结点cur_e赋值为value
    Parent(T, cur_e):若cur_e是树T的非根结点,则返回它的父结点
    LeftChild(T, cur_e):若cur_e是树的非叶结点,则返回它的最左孩子
    RightSibling(T, cur_e):若cur_e有右兄弟,则返回它的右兄弟否则返回空
    InsettChild(*T,*p, i, c):其中p指向树T的某个结点,i为所指结点p的度数加1,非空树c与T不相交,操作结果为插入c为树T中p指向结点的第i棵子树
    DeleteChild(*T, *p, i):其中p指向树T的某个结点,i为所指结点p的度,操作结果为删除T中p所指向结点的第i棵子树

树的存储结构

根据附加信息的不同,分为:双亲表示法、孩子表示法、和孩子兄弟表示法
以下分别称为:父结点表示法、子结点表示法、和兄弟结点表示法;这些方法可能会综合顺序存储和链式存储

父结点表示法

每个结点都存有其父结点的位置信息(根结点改值为-1)

typedef struct PTNode  /*结点结构*/
{
    int data;  /*结点数据*/
    int parent;  /*父结点位置*/
}PTNode;
typedef struct  /*树结构*/
{
    PTNode nodes[MAX_TREE_SIZE];  /*结点数组*/
    int r, n;  /*根的位置和结点树数*/
}PTree;


子结点表示法

也叫“多重链表表示法”

“子结点表示法”势必要引入额外的信息来表示子结点的位置,然而每个结点的子结点个数可能是不一样的,所以对于每个结点而言,其存储的子结点位置信息的个数(结点的度)也可能是不一样的;对此,有两种解决方案:
方案一:指针域的个数等于树的度
方案二:指针域的个数等于该结点的度
方案一对于树中各结点度相差很大时,显然是浪费空间的,因为有很多结点的指针域都是空的;不过如果树的各个结点度相差很小时,那就意味着开辟的空间被充分利用了,所以这种存储结构适合树中各度相差不大的情况
方案二克服了潜在的空间浪费,但是由于各个结点的链表是不同的结构,加上要维护结点的度的数值,在运算上会带来时间上的损耗

子结点表示法最终方案:
把每个结点子结点排列起来,以单链表作为存储结构,则n个结点有n个子结点链表,如果是叶结点则此单链表为空,然后n个头指针又组成一个线性表,采用顺序存储结构,放进一个一维数组中

typedef struct _ctnode  /*子结点*/
{
    int child;
    struct _ctnode *next;
}ChildPtr;
typedef struct  /*表头结构*/
{
    int data;
    ChildPtr firstchild;
}CTBox;
typedef struct  /*树结构*/
{
    CTBox nodes[MAX_TREE_SIZE];  /*结点数组*/
    int r, n;  /*根的位置和结点数*/
}CTree;

这样的结构,如果要查找某个结点的某个子结点,或者某个结点的兄弟结点,只需要查找这个结点的子结点单链表即可;对于遍历整棵树也是很方便的,对头结点的数组循环即可

说明:综合“父结点表示法”和“子结点表示法”,可以设计出“父子结点表示法”,在子结点单链表中加入其父结点位置的指针域即可

兄弟结点表示法

注意到任意一棵树,它的结点的第一个孩子如果存在就是唯一的,同样它的右兄弟如果存在也是唯一的,可以用两个指针分别指向这个结点的长子结点和右兄弟结点,如果不存在就指向空

typedef struct _csnode
{
    int data;
    struct _csnode *firstchild, *rightsib;
}CSNode, *CSTree;

同样的可以结合“兄弟结点表示法”和“父结点表示法”,这样便于查找某个结点的父结点;因为纯粹是体力活,这里不再赘述


二叉树简介

说明:本文旨在从宏观上介绍作为数据结构的树,有关二叉树的具体实现和技巧等将在后续文章中写出;此处只是作为一个抛砖引玉的作用罢了

二叉树是n(n≥0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别为根结点的左子树和右子树的二叉树组成

二叉树的种类繁多,比如普通二叉树、满二叉树、完全二叉树、平衡二叉树树、二叉搜索树、红黑树、哈夫曼树等

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