树是n(n>=0)个节点的有限集。
树的特点:
- 有且仅有一个特定的根节点
- 当n>0时,其余节点可分为m(m>0)个不相交的有限集N1,N2....Nn,其中每个集合本身又是一棵树,叫做子树。
- n=0时,为空树
树的表示方法:
- 树形表示
- 嵌套表示
- 广义表表示
- 凹入表示法
树的常用术语:
- 树的结点包含一个数据元素及若干个指向其子树的分支
- 节点拥有的子树称为节点的度
- 度为0的节点称为:叶子或终端节点
- 度不为0的节点称为:非终端节点或分支节点
- 树的度:树内节点的度的最大值
- 层次:从根开始定义,根为第一层,根的孩子为第二层
- 深度和高度:树中节点的最大层次
- 有序树和无序树:左右节点是有次序的(不能交换)是有序树,反之是无序树。
- A的度为3,B的度为2,c的度为1
- K L M I J 为叶子,度为0
- 树的度为:3(3为树内最大的度)
- 树的深度为:4
森林:
森林是m棵互不相交的树的集合,对树中每一个节点而言,其子树的集合为森林。
树一定是森林,森林不一定为树。
树和森林的区别:
树只有一个根节点,森林可以有多个根节点。
二叉树:
二叉树:节点至多有两颗子树(不存在度大于2的节点),二叉树的子树有左右之分,次序不能颠倒,二叉树也可以为空树。
二叉树的5种基本形态:
二叉树的性质:
- 二叉树的第i层最多有: 2^(i-1) 个节点(i>=1)
- 深度为K的二叉树最多有:2^K-1 个节点(K>=1)
- 对任意一颗二叉树T,其终端节点为N0,度为2的节点个数为N2,则N0=N2+1。
- 具有n个节点的完全二叉树的深度为 log2(n)+1
二叉树的特殊形态:
- 满二叉树:深度为k,且有 2^(i-1) 个节点的二叉树
- 完全二叉树:深度为k,有n个节点的二叉树,仅当其每一个节点都与深度为k的满二叉树中的节点位置一 一对应。叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部
- 满二叉树是完全二叉树的特殊形态
已知节点数求二叉树的种类:
- 使用卡特兰数:n为节点个数
二叉树的存储结构:
顺序存储:
用一个数组进行存储,不存在的节点置为0。
#define Max_Size 100//二叉树的最大节点数
template<class T>
class tree
{
private:
T tree[Max_size];//节点数组
};
这种方法只适用于完全二叉树,非完全二叉树的话空间浪费严重。
深度为k且只有k个节点的单支树需要2^k-1的一维数组。
链式存储:
节点的结构为:
- 第一种:不需要parent指针(二叉链表)
- 第二种: 需要parenr指针(三叉链表)
二叉链表:
template<class T>
class tree_node
{
private:
T _data;//数据
tree_node<T>* _leftchild;//左孩子指针
tree_node<T>* _rightchild;//右孩子指针
};
三叉链表:
template<class T>
class tree_node
{
private:
T _data;//数据
tree_node<T>* _leftchild;//左孩子指针
tree_node<T>* _rightchild;//右孩子指针
tree_node<T>* _parent;//父类指针
};
两种的表示方法的示意图:
n个节点的二叉链表拥有n+1个空链域
n个节点的二叉链表用到的链域为n-1个(除了根节点)
三叉链表同理:用到的链域为:2*(n-1) 空链域为:n+2=3n- 2*(n-1)
遍历二叉树:
- 先序遍历:
- 根节点
- 先序遍历左子树
- 先序遍历右子树
- 中序遍历:
- 中序遍历左子树
- 根节点
- 中序遍历右子树
- 后序遍历:
- 后序遍历左子树
- 后序遍历右子树
- 根节点
- 层次遍历:从上到下,从左到右访问。
- 已知前序遍历和中序遍历,可以确定一棵二叉树
- 已知中序遍历和后序遍历,可以确定一棵二叉树
- 已知前序遍历和后序遍历,无法确定一棵二叉树
先序遍历:
void travPre_R(binTree* BT)//先序遍历
{
if (BT)//结点不为空
{
printf("%c", BT->data);//输出根节点
travPre_R(BT->leftchild);//遍历左子树
travPre_R(BT->rightchild);//遍历右子树
}
}
中序遍历:
void travPre_R(binTree* BT)//中序遍历
{
if (BT)//结点不为空
{
travPre_R(BT->leftchild);//遍历左子树
printf("%c", BT->data);//输出根节点
travPre_R(BT->rightchild);//遍历右子树
}
}
后序遍历:
void travPre_R(binTree* BT)//后序遍历
{
if (BT)//结点不为空
{
travPre_R(BT->leftchild);//遍历左子树
travPre_R(BT->rightchild);//遍历右子树
printf("%c", BT->data);//输出根节点
}
}
线索二叉树:
在二叉树的结点上加上线索的二叉树。
如果某个节点的左孩子为空,将空的左孩子指针域改为指向其前驱,如果某个节点的右孩子为空,将空的右孩子指针域改为其后继,这种指向的指针称为线索。
线索二叉树:加上线索的二叉树
线索二叉树的前中后序的画法:
- 按照你选择的遍历顺序找到相应节点的前驱和后继,
- 把为空的左孩子指向它的前驱
- 把为空的右孩子指向它的后继
- 没有前驱或后继为nullptr
节点结构为:
enum P{Link,Thead}
class treenode
{
private:
int data;
treenode* lchild,*rchild;
P LTag,RTag;左右标记
};
线索链表:由以上节点所构成的二叉链表作为二叉链表的存储结构
以下列举:前序遍历的示意图,中序和后序的类似就不列举了 。
,,,,,,,,,,,,,
优点:
- 结点的插入和删除麻烦,且速度也较慢。
- 线索子树不能共用。