树(Tree)是n(n≥0)个结点的有限集。n=O时称为空树。
在任意一棵非空树中:
(1)有且仅有一个特定的称为根( Root)的结点;
(2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1、T2、……、Tm,其中每一个集合本身又是一棵树,并且称为根的子树(SubTree )
1)n>0时,根节点是唯一的
2)m>0时,子树的个数没有限制,但是子树一定是互不相交的
data:数据域,存储结点的数据信息
parent:指针域,存储该结点的双亲在数组中的下标
每个结点有多个指针域,其中每个指针指向一颗子树的根结点,我们吧这种方法叫做多重链表表示法
方案1:指针域的个数等于树的度。
如果树中各结点的度相差很大时,是很浪费空间的,有很多的结点,指针域都是空的,但是当各结点的度相差很小时,那就意味开辟的空间被充分的利用
方案2:每个结点的指针域的个数等于该结点的度,专门取一个位置存储结点指针域的个数
克服浪费空间的缺点,对空间的利用率提高,但是由于各结点的链表是不同的结构,还需要维护结点度的数值,带来了时间上的损耗
- 孩子表示法: 每个结点的孩子结点排列起来,以单链表做存储结构,则n个结点有n个孩子链表,叶子结点则表示该单链表为空,然后n个头指针组成一个线性表,采用顺序存储结构,存放进一维数组中
任意一棵树,它的结点的第一个孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的,因此设计两个指针,分别指向该结点的第一个孩子和此结点的右兄弟
满足两个条件:
- 本身是有序树
- 树中包含的各个结点的度不能超过2,只能是0,1,2
二叉树中除了叶子结点,每个结点的度都为2,则此二叉树称为满二叉树
二叉树中除去最后一层节点为满二叉树,且最后一层的结点依次从左到右分布,则此二叉树被称为完全二叉树
完全二叉树从右向左依次标号,对于任意结点i来说:
顺序存储、链式存储
使用顺序表(数组)存储二叉树,顺序存储只适用于完全二叉树,如果想顺序存储普通二叉树,则需要提前将普通二叉树转化为完全二叉树
完全二叉树的顺序存储仅需从根结点开始,按照层次依次将树中结点存储到数组即可
若结点i有左右孩子,则其左孩子结点为2i,右孩子结点为2i+1
顺序存储二叉树,如果是普通二叉树会造成空间浪费的现象。
结点结构:
typedef struct BiTNode
{
ElemType data;
BiTNode* Lchild; //左孩子
BiTNode* Rchild; //右孩子
}BiTNode,*BiTree;
将二叉树的每个结点的空指针引出一个虚结点,它的值是特定值,比如‘#’,称这种处理后的二叉树为原二叉树的扩展二叉树。
//递归建立
void CreateBiTree(BiTree* T) //AB#D##C##扩展二叉树
{
ElemType ch;
cin >> ch;
if (ch == '#')
*T = NULL;
else {
*T = (BiTree)malloc(sizeof(BiTNode));
if (!*T)
{
exit(OVERFLOW);
}
(*T)->data = ch;
CreateBiTree(&(*T)->Lchild); //构造左子树
CreateBiTree(&(*T)->Rchild); //构造右子树
}
}
void PreOrderTraverse(BiTNode* Root) //前序遍历 递归实现
{
if (Root == NULL) return;
cout << Root->data << " ";
PreOrderTraverse(Root->Lchild);
PreOrderTraverse(Root->Rchild);
}
void InOrderTraverse(BiTNode* Root) //中序遍历 递归实现
{
if (Root == NULL) return;
InOrderTraverse(Root->Lchild);
cout << Root->data << " ";
InOrderTraverse(Root->Rchild);
}
void PostOrderTraverse(BiTNode* Root) //后序遍历 递归实现
{
if (Root == NULL) return;
PostOrderTraverse(Root->Lchild);
PostOrderTraverse(Root->Rchild);
cout << Root->data << " ";
}
例题: 前序遍历序列为ABCDEF,中序遍历序列CBAEDF,请问后续遍历序列是?
先分析出A是根结点,那么从中序可以看出 B,C是A的左子树上结点,D,E,F是A的右子树上结点,在看B,C前序先输出B,则B是A的左孩子,C是B的结点,从中序可以看出先输出C,在输出B,则C是B的左孩子,同理前序可以看出D是A的右孩子,E,F是D的孩子结点,中序可以看出先输出E,D,F,则E是D的左孩子,F是D的右孩子。
最终二叉树为
指向前驱和后继电脑指针称为线索,加上线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树
typedef enum{Link,Thread} PointerTag;
//link==0表示指向左右孩子指针
//thread==1表示指向前驱或后继的线索
typedef struct BiThrNode
{
ElemType data; //数据
BiThrNode *Lchild,*Rchild;
PointerTag Ltag;
PointerTag Rtag;
};
步骤: