树的定义:(使用递归)
树是n个结点的有限集。n=0时称为空树。在任意一棵非空树中:(1) 有且仅有一个特定的称为根的结点;(2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1,T2,......,Tm,其中 每一个集合本身又是一棵树,并且称为根的子树。
例如下图1:
子树T1和子树T2是根结点A的子树。
D、G、H、I组成的树是B为根结点的子树,E、J组成的树是以C为根结点的子树。
例如下图2:
(1)n>0时根结点是唯一的。
(2)m>0时,子树的个数没有限制,但它们一定是互不相交的。
结点分类:
树的结点:由 一个数据元素和 若干指向其子树的分支组成。度:结点拥有的子树数。
叶结点(终端结点):度为0的结点。
分支结点(非终端结点):度不为0。
根结点和分支结点统称为内部结点。
树的度:树内各结点的度的最大值。
例如下图3:
结点间关系:
结点的孩子:结点的子树的根。同时也为孩子的双亲。兄弟:同一个双亲的孩子之间,关系为兄弟。
结点的祖先:从根到此结点所经分支上的所有结点。
结点的子孙:以此结点为根的子树中任一结点。
其他概念:
树的 深度(高度):树中结点的最大层次。有序树:树中结点的各子树从左到右是有次序的不能互换。反之,为无序树。
森林:是m(m>0)棵互不相交的树的集合。
对于树中每个结点而言,其子树的集合即为森林。(如图2的T1,T2)
三种不同表示法:
(1) 双亲表示法对于每一个结点,它不一定有孩子,但是一定有且仅有有一个双亲。
**解释: 假设以一组连续空间存储树的结点,同时 在每个结点中,附设一个指示器指示其双亲结点在数组中的位置。
**其分布情况如图:
data | parent
data:数据域。parent:指针域,存储该结点的双亲在数组中的下标。
**代码如下:
/*树的双亲表示法的结点结构定义*/
#define MAX_TREE_SIZE 100
typedef int TElemType;
typedef struct PTNode /*结点结构*/
{
TELemType data; /*结点数据*/
int parent; /*双亲位置*/
}PTNode;
typedef struct
{
PTNode node[MAX_TREE_SIZE]; /*结点数组*/
int r,n; /*根的位置和结点数*/
}PTree;
规定:根的结点位置域(无双亲)设置为-1。
孩子(长子,要求孩子0到1个)或兄弟关系(右兄弟,要求孩子2个以内)均可通过下标来体现,若无长子域或无右兄弟,指针域设定位-1。
(2)孩子表示法
**解释:
把每个结点的孩子结点排列起来,以单链表作存储结构,则n个结点有n个孩子链表。如果是叶子结点则此单链表为空。然后n个头指针由组成一个线性表(表头数组),采用顺序存储结构,存放入一个一维数组。
**其分布情况如下:
(表头结点)
data | firstchild
firstchild是头指针,类似于struct CTNode *firstchild。
(孩子链表的孩子结点)
child | next
child是数据域,用来存储某个结点在表头数组中的下标。next指针域,用来存储指向某结点的下一个孩子结点的指针。
**代码如下:
/*树的孩子表示法结构定义*/
#define MAX_TREE_SIZE 100
typedef struct CTNode /*孩子结点*/
{
int child;
struct CTNode *next;
}*ChildPtr;
typedef struct /*表头结构*/
{
TELemType data;
ChildPtr firstchild; /*定义头指针*/
}CTBox;
typedef struct
{
CTBox nodes[MAX_TREE_SIZE]; /*结点数组(表头数组)*/
int r,n; /*根的位置和结点数*/
}CTree;
用途:
- 寻找某结点的孩子结点
- 寻找某结点的兄弟结点
(3)孩子兄弟表示法
**解释:
任意一棵树,它的结点的第一个孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的。
因此,可设置两个指针,分别指向该结点的第一个孩子和此结点的兄弟。
**其分布情况如下:
data | firstchild | rightsib
firstchild:指针域,存储该结点的第一个孩子结点的存储地址; rightsib:指针域,存储该结点的右兄弟结点的存储地址。
**代码如下:
/*树的孩子兄弟表示法结构定义*/
typedef struct CSNode
{
TElemType data;
struct CSNode *firstchild,*rightsib;
}CSNode,*CSTree;
- 优势:将一棵复杂的树变成了一棵二叉树。
二叉树的定义:
二叉树是n(n>=0)个结点的有限集合,该集合或者为空集(空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。二叉树特点:
- 每个结点最多有两棵子树,二叉树中不存在度大于2的结点。
- 左子树和右子树有顺序,次序不能任意颠倒。
- 树中某结点只有一棵子树,也要区分它是左子树还是右子树。
二叉树的五种基本形态:
- 空二叉树。
- 只有一个根结点。
- 根结点只有左子树。
- 根结点只有右子树。
- 根结点既有左子树又有右子树。
特殊二叉树:
斜树: 所有的结点都只有左子树(或只有右子树)的二叉树叫左(右)斜树。
特点:每一层都只有一个结点,结点的个数与二叉树的深度相同。
满二叉树:在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层。
特点:
- 叶子只能出现在最下一层。
- 非叶子结点的度一定是2。
- 在同样深度的二叉树中,满二叉树的结点个数最多,叶子数最多。
完全二叉树:对一棵具有n个结点的二叉树按层序编号,如果编号为i(1<=i<=n)的结点与同深度的满二叉树中编号为i的结点在二叉树中位置完全相同。
若按层编号从左到右,编号存在空档,则不是完全二叉树。若编号是连续的,则是。
特点:
- 叶子结点只能出现在最下两层。
- 最下层的叶子一定集中在左部连续位置。
- 倒数二层,若有叶子结点,一定都在右部连续位置。
- 如果结点度为1,则该结点只有左孩子,既不存在只有右子树的情况。
- 同样结点数的二叉树,完全二叉树的深度最小。(不能再小)
满二叉树一定是一棵完全二叉树,但完全二叉树不一定是满的。
二叉树的性质:
- 第i层上至多有2^(i-1)个结点。(i>=1)
- 深度为k的二叉树至多有2^k-1个结点。(k>=1)
- 对任意一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1。
- 具有n个结点的完全二叉树的深度为【log2n】+1。(【X】表示不大于X的最大整数)
- 如果对一棵有n个结点的完全二叉树的结点按层序编号(从第1层到第【log2n】+1层,每层从左到右),对任一结点i(i>=1&&i<=n)有:
1.如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲是结点【i/2】。
2.如果2i>n,则结点i无左孩子(结点i为叶子结点);否则其左孩子是结点2i。
3.如果2i+1>n,则结点i无右孩子;否则其右孩子是结点2i+1。
二叉树的存储结构:
顺序存储结构适用于完全二叉树。
二叉链表:设计一个数据域和两个指针域。
结点结构图:
lchild | data | rchild
代码如下:
/*二叉树的二叉链表结点结构定义*/
typedef struct BiTNode /*结点结构*/
{
TElemType data; /*结点数据*/
struct BiTNode *lchild,*rchild; /*左右孩子指针*/
}BiTNode,*BiTree;