定义和基本术语
树(tree):是n(n>=0)个结点的有限集。在任意一颗非空树中:有且只有一个称为根(root) 的结点。其余的结点被分为m(m>=0)个互不相交的有限集,其中每个集合本身又是一棵树,称为根结点的子树(sub_tree)。
树的表示法:有图示法、集合表示法、广义表表示法和缩进表示法。图示法表示的二叉树中,边的数目(或称分枝数,用e表示)恰好比结点数目(用n表示)少一个,即e = n - 1。
结点的分类:从计算机角度可分为终端结点额非终端结点;以树的特征来分可分为根结点、分支结点和叶子结点;用族谱的关系来分,可以分为双亲结点和孩子结点、祖先结点和子孙结点、兄弟结点和堂兄弟结点。
度:分为结点的度和树的度。结点的度指与该结点相连接的孩子结点的数目。树的度指树中所有结点度的最大值。
深度: 结点的深度(或称层次) 指从根开始至该结点的层数。树的深度(或高度) 指该树中结点层次最大值。
有序树和无序树:若树中结点的各颗子树是从左到右有次序的(不能互换),则为有序树,否则为无序树。
有向树和无向树:如果树的每个分支都是有方向的则为有向树,否则为无向树。
n元树:树的度为n的有向树
位置树:是一棵有向树。如果树中结点的每个孩子结点的位置是不能被改变的(改变则不是原树),则称其为位置树。
m叉树:树的度为m的有向位置树。即m元位置树。
森里:是m(m>=0)颗互不相交的树的集合。对于树中的每个结点而言,其子树的集合就是森林。森林中的树也可以有顺序关系和位置关系。
树的基本操作
1->InitTree(T)——构造一颗空树T
2->ClearTree(T)——将已经存在的树T清空
3->TreeEmpty(T)——判断树T是否为空树
4->InsertChild(T,p,i,c)——在树T中插入子树c,使其成为p指向的结点的第i颗子树。
5->DeleteChile(T,p,i)——删除树T中p所指向的结点的第i颗子树
6->TraverseTree(T)——按某种次序对树T中的所有结点进行访问,每个结点仅访问一次
二叉树
一种特殊的有向树,又称二元位置树。每个结点至多有两颗子树。确切定义:二叉树或者为空、或者由一个根结点加上两颗分别称为左子树和右子树的、互不相交的二叉树组成。
满二叉树
除叶子结点外的任何结点均有两个孩子结点,且所有的叶子结点都在同一层上。每一层上的结点数都是最大的。
完全二叉树
除去最底层结点后的二叉树是一棵满二叉树,且最底层结点均靠左对齐,即左边没有空隙再放入任何一个结点。
实质上满二叉树是完全二叉树的一个特例。
性质
1->在二叉树的第i(i>=1)层上至多有2^i-1个结点
2->深度为k(k>=1)的二叉树上至多有(2^k)-1个结点
3->任意一颗二叉树中,叶子结点的数目(n0)总比度为2的结点的数目(n2)多一个,即n0=n2+1。
4->具有n个结点的完全二叉树的深度为[log2 n]向下取整+1
5->有n个结点的完全二叉树,若按照从上至下、从左至右的顺序对二叉树中的所有结点从1开始顺序编号,则对于序号为i(1<=i<=n)的结点,有:(1)其双亲结点编号为[i/2] 向下取整(2)其左孩子结点的编号为2i (3)其右孩子结点的编号为2i+1
6->当结点个数为偶数时,完全二叉树中有且仅有一个度为1的结点;当结点个数为奇数,完全二叉树中没有度为1的结点。
7->完全二叉树中编号大于n/2向下取整的结点均为叶子结点
二叉树的存储
顺序存储结构
按从上至下、从左至右的顺序存储到一个线性结构(通常为数组)中。为了方便计算,需增加一些虚结点,使之变成完全二叉树的树形。
数组中下标为0的单元可用于存放满二叉树中结点的总数,或二叉树的深度,虚结点可以用一个特殊的标志来识别。
二叉树的结构存储类型说明
#define VirNode '0'//定义虚结点值
#define MAX_TREE_SIZE 100//定义存储空间最大容量
typedef char ElemType;//定义结点类型值
typedef ElemType SqBitTree[MAX_TREE_SIZE];//SqBitTree[0]单元存放结点的总数,通常存放构成满二叉树时的结点总数
链式存储结构
二叉链表:每个结点上有两个指针域,分别指向其左子树和右子树
三叉链表:在二叉链表的基础上增加一个指针域指向其双亲结点
通过遍历等手段可以将一个二叉链表改为线索链表
二叉链表的存储类型说明
#include <stdio.h>
typedef char ElemType;//数据域值类型
typedef struct Node
{ElemType data;//数据域
struct Node *lchild,*rchild;//左右指针域,分别存储左右孩子的存储位置
}BitTree;
「注」:具有n个结点的二叉链表中一定有n+1个空链域
二叉树的遍历及应用
二叉树遍历就是一次访问二叉树中的各个结点,每个结点仅访问一次。并非一定是输出结点数据,还可能是查看结点属性值、更新结点数据值、增加或删除结点等。
遍历的过程应该是递归的,以L、D、R分别表示遍历左子树、访问根结点和遍历右子树,则可有LDR、LRD、DLR、DRL、RLD、RDL6种遍历方式,规定先左后右,就有了DLR、LDR和LRD这三种遍历方式。根据根结点在遍历时的访问顺序,分别称为二叉树的先(根)序遍历、中(根)遍历和后(根)遍历,递归的算法定义和描述如下。
递归遍历定义
先序遍历二叉树DLR,若二叉树非空则1.访问根结点2.先序遍历根的左子树3.先序遍历根的右子树
中序遍历二叉树LDR,若二叉树非空则1.中序遍历根的左子树2.访问根结点3.中序遍历根的右子树
后序遍历二叉树LRD,若二叉树非空则1.后序遍历根的左子树2.后序遍历根的右子树3.访问根结点
递归遍历算法
先序遍历二叉树的递归算法
void PreOrder(BitTree *bt)
{if(bt != NULL)
{printf("%c",bt->data);//访问根结点
PreOrder(bt->lchild);//先序遍历根结点的左子树
PreOrder(bt->rchild);//先序遍历根结点的右子树
}
}
中序遍历与后序遍历的递归算法仅需改变上述中间三行的顺序即可。
树和森林
树的存储结构
双亲表示法:用一组连续的空间来存储树上的结点,同时在每个结点上附加一个指示器来指明其双亲结点所在的位置
每个结点(除根结点)有且仅有一个双亲结点,通过parent域查找任何结点的双亲,但在查找孩子结点时需要遍历整个表。
类型定义如下
typedef char TElemType;
#define MAX_TREE_SIZE 100
typedef struct
{ElemType data;
int parent;
}PTNode;
typedef struct
{PTNode nodes[MAX_TREE_SIZE];
int n;
}PTree;
孩子链表示法:用一组连续的空间来存储树上的结点,同时在每个结点上附加一个指针指向其孩子结点构成的单链表。
找孩子结点只需搜索fistchild指针指向的单链表即可,但找某一结点的双亲结点就比较困难了,需要搜索所有的单链表。
其类型定义如下:
typedef char TElemType;
#define MAX_TREE_SIZE 100
typedef struct CTNode
{int child;
struct CTNode *next;
}CTNode,*Childptr;
typedef struct
{ElemType data;
ChildPtr firstchild;
}CTBox;
typedef struct
{CTBox nodes[MAX_TREE_SIZE];
int n;
}CTree;
孩子双亲表示法:用一组连续的空间来存储树上的结点,同时在每个结点上附加一个提示器来提示其双亲结点的位置,再附加一个指针指向其孩子结点构成的单链表。
这样既能很快的找到每个结点的双亲结点,又能很快的找到孩子结点,但在空间上略显浪费。
其类型定义如下:
typedef char ElemType;
#define MAX_TREE_SIZE 100
typedef struct CTNode
{int child;
struct CTNode *next;
}CTNode,*ChildPtr;
typedef struct
{ElemType data;
int parent;
ChildPtr firstchild;
}PTNode;
typedef struct
{PCNode nodes[MAX_TREE_SIZE];
int n;
}PCTree;
孩子兄弟表示法:以二叉链作为存储结构来表示树和森林的一种结构,其中每个结点的两个指针分别指向其第一个孩子结点和下一个兄弟结点。
其类型定义如下:
typedef char ElemType;
typedef struct CSNode
{ElemType data;
struct CSNode *firstchild,*nextsibling;
}CSNode,*CSTree;
树和森林的遍历
树的遍历
1.先根遍历:若树非空,则先访问根结点,再依次先根遍历其每一颗子树。
2.后根遍历:若树非空,则先依次遍历其每一颗子树,再访问根结点。
3.层次遍历:若树非空,则按从上到下、从左至右的顺序依次访问树中的每一个结点。
森林的遍历
一:先序遍历
若森林非空,则可按如下规则进行遍历:
1.访问森林中第一棵树的根结点
2.先序遍历第一棵树中根结点的子树森林
3.先序遍历其余的树构成的森林
二:后序遍历
若森林非空,则可按如下规则进行遍历:
1.后序遍历第一棵树中根结点的子树森林
2.访问第一棵树的根结点
3.后序遍历其余的树构成的森林