在树中常常将数据元素称为结点。
树是n(n>=0)个结点的有限集合。
某结点所拥有的子树的个数称为该结点的度。
树中各结点度的最大值称为该树的度。
度为零的结点称为叶子结点,也称为终端结点。
度不为零的结点称为分支结点,又称为非终端结点。
某结点的子树的根结点称为该结点的孩子结点。繁殖该结点称为其孩子结点的双亲结点。
具有同一个双亲的孩子结点互称兄弟结点。
路径:如果树的结点序列n1, n2,…, nk有如下关系:结点ni是ni+1的双亲(1<=i<k),则把n1, n2, …, nk称为一条由n1至nk的路径;
路径上经过的边的个数称为路径长度。
如果从结点x到结点y有一条路径,那么x就称为y的祖先,y称为x的子孙。
树中所有结点的最大层数称为树的密度。
M棵互不相交的树的集合称为森林。
树的遍历:从根结点出发,按照某种次序访问树中所有的结点,使得每个结点被访问一次且仅被访问一次。
二叉树和树是两种树结构。
左斜树:所有结点都只有左子树的二叉树称为左斜树。
右斜树:所有结点都只有右子树的二叉树称为右斜树。
左斜树和右斜树统称为斜树。
满二叉树:所有分支结点都存在左子树和右子树,且所有叶子都在同一层上,这样的二叉树称为满二叉树。
完全二叉树:在满二叉树中,从最后一个结点开始,连续删除任意个结点,即为完全二叉树。
完全二叉树的特点:
1. 叶子结点只能出现在最下两层且最下层的叶子结点都集中在二叉树的左面;
2. 完全二叉树中如果有度为1的结点,只可能有一个,且该结点只有左孩子。
3. 深度为k的完全二叉树在k-1层上一定是满二叉树。
4. 在同样结点个数的二叉树中,完全二叉树的深度最小。
完全二叉树的基本性质:
二叉树的第i层上最多有2i-1个结点(i≥1)。
一棵深度为k的二叉树中,最多有2k-1个结点,最少有k个结点。
在一棵二叉树中,如果叶子结点数为n0,度为2的结点数为n2,则有: n0=n2+1。
具有n个结点的完全二叉树的深度为log2n +1。
对一棵具有n个结点的完全二叉树中从1开始按层序编号,则对于任意的序号为i(1≤i≤n)的结点(简称为结点i),有:
(1)如果i>1,则结点i的双亲结点的序号为 i/2;如果i=1,则结点i是根结点,无双亲结点。
(2)如果2i≤n,则结点i的左孩子的序号为2i;
如果2i>n,则结点i无左孩子。
(3)如果2i+1≤n,则结点i的右孩子的序号为2i+1;如果2i+1>n,则结点i无右孩子。
孩子链表的基本思想:把每个结点的孩子排列起来,看成是一个线性表,且以单链表存储,则n个结点共有n 个孩子链表。这n 个单链表共有n 个头指针,这n 个头指针又组成了一个线性表,为了便于进行查找采用顺序存储。最后,将存放n 个头指针的数组和存放n个结点的数组结合起来,构成孩子链表的表头数组。
前序遍历递归算法
template <class DataType>
void BiTree<DataType> ::PreOrder(BiNode<DataType> *bt)
{
if (bt == NULL) return; //递归调用的结束条件
else {
cout << bt->data; //访问根结点bt的数据域
PreOrder(bt->lchild); //前序递归遍历bt的左子树
PreOrder(bt->rchild); //前序递归遍历bt的右子树
}
}
中序遍历递归算法
template <class DataType>
void BiTree<DataType> :: InOrder(BiNode<DataType> *bt)
{
if (bt == NULL) return; //递归调用的结束条件
else {
InOrder(bt->lchild); //中序递归遍历bt的左子树
cout << bt->data; //访问根结点bt的数据域
InOrder(bt->rchild); //中序递归遍历bt的右子树
}
}
后序遍历递归算法
template <class DataType>
void BiTree<DataType> ::PostOrder(BiNode<DataType> *bt)
{
if (bt == NULL) return; //递归调用的结束条件
else {
PostOrder(bt->lchild); //后序递归遍历bt的左子树
PostOrder(bt->rchild); //后序递归遍历bt的右子树
cout << bt->data; //访问根结点bt的数据域
}
}
层序遍历
template <class DataType>
void BiTree<DataType> ::LeverOrder( )
{
front = rear = -1; //采用顺序队列,并假定不会发生上溢
if (root == NULL) return; //二叉树为空,算法结束
Q[++rear] = root; //根指针入队
while (front != rear) //当队列非空时
{
q = Q[++front]; //出队
cout << q->data;
if (q->lchild != NULL) Q[++rear] = q->lchild;
if (q->rchild != NULL) Q[++rear] = q->rchild;
}
}
线索:将二叉链表中的空指针域指向前驱结点和后继结点的指针被称为线索;
线索化:使二叉链表中结点的空链域存放其前驱或后继信息的过程称为线索化;
线索链表:加上线索的二叉链表称为线索链表。
前序遍历非递归算法
template <class DataType>
voidBiTree::PreOrder(BiNode<DataType> *root)
{
top = -1; //采用顺序栈,并假定不会发生上溢
while (root != NULL || top != -1)
{
while (root != NULL)
{
cout<<root->data;
s[++top] = root;
root = root->lchild;
}
if (top != -1) {
root = s[top--];
root = root->rchild;
}
}
}
中序遍历非递归算法
template <class DataType>
voidBiTree::PreOrder(BiNode<DataType> *root)
{
top = -1; //采用顺序栈,并假定不会发生上溢
while (root != NULL || top != -1)
{
while (root != NULL)
{
s[++top] = root;
root = root->lchild;
}
if (top != -1) {
root = s[top--];
cout<<root->data;
root = root->rchild;
}
}
}
后序遍历非递归算法
template <class DataType>
void BiTree<DataType> :: PostOrder(BiNode<DataType>*bt)
{
top = -1; //采用顺序栈,并假定栈不会发生上溢
while (bt != NULL || top != -1) //两个条件都不成立才退出循环
{
while (bt != NULL)
{
top++; s[top].ptr = bt;s[top].flag = 1; //root连同标志flag入栈
bt = bt->lchild;
}
while (top != -1 && s[top].flag == 2)
{
bt = s[top--].ptr; cout <<bt->data;
}
if (top != -1) {
s[top].flag = 2; bt =s[top].ptr->rchild;
}
}
}
森林转化为二叉树
(1)将森林中的每棵树转换成二叉树;
(2)从第二棵二叉树开始,依次把后一棵二叉树的根结点作为前一棵二叉树根结点的右孩子,当所有二叉树连起来后,此时所得到的二叉树就是由森林转换得到的二叉树
叶子结点的权值:对叶子结点赋予一个有意义的数值量。
带权路径长度:从根结点到各个叶子结点的路径长度与相应结点权值的乘积之和。
哈夫曼树:给定一组确定权值的叶子结点,带权路径长度最小的二叉树。
哈夫曼树的特点:
1. 权值越大的叶子结点越靠近根结点,而权值越小的叶子结点越远离根结点。
2. 只有度为0(叶子结点)和度为2(分支结点)的结点,不存在度为1的结点.
深入理解树结构与算法
本文详细阐述了树结构的概念,包括结点、度、路径、路径长度、祖先与子孙等基本概念,以及树的遍历方法(前序、中序、后序遍历)和非递归算法实现。此外,文章还介绍了二叉树和树的特殊类型,如左斜树、右斜树、满二叉树、完全二叉树及其特点。最后,通过讨论完全二叉树的性质和孩子链表的基本思想,深入探讨了树结构的应用与优化。

被折叠的 条评论
为什么被折叠?



