基础知识
基本概念
树
包含n个数据元素的有限集合
- n=0:空树
- n>0:有且仅有一个没有前驱,只有后继的结点,被称为根(root)
【树本质就是一个族谱一样的东西,这里拿灰太狼家举个例子】
结点
树里的每一个元素称为一个结点
【族谱里的人,比如灰太狼、夜太郎……】
根节点
没有前驱的,第一层的结点
【相当于武大狼在狼群,或者软绵绵在羊群的地位】
终端节点/叶子节点
度为0的结点
【未婚未孕的人,比如小灰灰】
非终端节点/分支节点/内部节点
度不为0的结点
【已婚已孕的人,比如灰太狼】
孩子/双亲
注意的是双亲虽然叫双亲,但是只是一个结点,其实叫父亲更贴切
你只有一个生父,所以双亲只有一个;
但你爸可以有很多孩子。
【小灰灰的(双亲)是灰太狼;灰太狼的(孩子)是小灰灰和他的弟弟妹妹】
兄弟
同一个爹
堂兄弟
双亲在同一层
【爸妈是同一辈,比如灰太狼和夜太郎】
!!!互为堂兄弟的结点,双亲不一定是兄弟,也有可能是堂兄弟
【比如小灰灰和夜太郎的孩子,也是堂兄弟】
祖先
从结点x往上到根节点途经的所有结点都是他的祖先
【你爸、你爷、你太爷都是你的祖先,但你二大爷不是】
子孙
以结点x为根的子树的所有结点都是他的子孙
结点的度
官方的回答是:一个结点的子树的数目
实际就是,他的后继/或者说分支的个数。
显然的,二叉树的 结点的度 只有0、1、2三种情况。
【结点的度就是孩子的个数,比如灰太狼的度就是5】
树的度
节点中最大的度为树的度。
!!!!max{ 结点的度 }
结点的层次
根节点为第一层
【辈分:灰太狼是第十八层,武大狼是第一层】
树的深度/高度
结点的最大层次。
【树的深度就是现在传到第几代,比如灰太狼是第十八代,那现在他们家族谱的深度就是十九(小灰灰这一层)】
有序树/无序树
- 有序:有次序,同一层从左到右
- 无序:同一层没有次序
森林
【两个没有联系的族谱】
二叉树
定义
二叉树定义
度为2的有序树
- 结点的度可以为0,1,2
- 有序!!!分左右结点/左右孩子
满二叉树定义
高度为 h 的树,有()个结点
- 结点的度为0、2
完全二叉树定义
从上到下、从左到右依次增加结点
- 满二叉树 是 完全二叉树
性质
二叉树性质
- 分支数 等于 结点数-1
- 已知高度h, 结点个数:
min: h
max: ()【满二叉树】
- 已知结点数n,高度:
min: 【不一定是完全二叉树】
max: n
完全二叉树性质
n 个节点,从上到下,从左到右,用 表示每个节点:
- 高度:
- i=0:根节点,无双亲
- i>0:双亲为
【!!!注意:从1开始表示的话:】
- 2*i+2<=n时,左孩子:2i+1; 右孩子:2i+2
- i为范围内奇数时,为左孩子;i为范围内偶数,为右孩子
【!!!注意:从1开始表示的话:相反(因为相当于整体-1)】
存储结构
顺序存储
定义:
采用一组连续存储空间(数组)从上到下,从左到右依次存储元素。
【满足上述完全二叉树的性质,但是把空的结点存为空】
优点:
- 可以方便计算出双亲和左右孩子的位置,快速访问
- 适用于存储完全二叉树
缺点:
- 一般二叉树,空间利用率低
【!!注意:使用自定义标记表示这个地方为空结点】
链式存储
链表表示法
二叉链表
leftChild | Data | rightChild |
依据上图,构造节点类
//二叉链表的结点类模板
template <class T>
struct bTreeNode
{
//数据成员
T data; //数据域
bTreeNode<T>* leftChild; //左孩子
bTreeNode<T>* rightChild; //右孩子
//函数成员
bTreeNode(); //无参的构造函数
bTreeNode(const T &d, bTreeNode<T>* LChild=NULL, bTreeNode<T>* RChild=NULL);
//已知元素、左右孩子
};
//无参的构造函数
template <class T>
bTreeNode<T>:: bTreeNode() //空结点
{
leftChild=NULL;
rightChild=NULL;
}
//含参的
template <class T>
bTreeNode<T>:: bTreeNode(const T &d, bTreeNode<T>* LChild=NULL, bTreeNode<T>* RChild=NULL)
{
data=d;
leftChild=LChild;
rightChild=RChild;
}
链表类模板
//最基本的二叉链表的链表类模板
template <class T>
class bTree
{
protected:
bTreeNode<T>* root; //根节点 //相当于普通链表类里的头结点
void Destroy(bTreeNode<T> *&r); //删除二叉树
public:
bTree(); //无参的构造函数
bTree(const &T r); //已知根节点元素的构造函数
virtual ~bTree(); //析构
};
具体函数
template <class T>
bTree<T>::bTree() //无参的构造函数
{
root = NULL;
}
template <class T>
bTree<T>::bTree(const &T rData) //已知根节点元素的构造函数
{
root->data = rData;
}
template <class T>
virtual bTree<T>:: ~bTree() //析构
{
Destroy(root);
}
template <class T>
void bTree<T>::Destroy(bTreeNode<T> *&r) // 操作结果:删除以r的二叉树
{
if(r != NULL) { // r非空,实施删除
Destroy(r->leftChild); // 删除左子树
Destroy(r->rightChild); // 删除右子树
delete r; // 删除根结点
r = NULL;
}
}
三叉链表
leftChild | Data | Partent | rightChild |
三叉链表的结点类模板
//二叉链表的结点类模板
template <class T>
struct bTreeNode
{
//数据成员
T data; //数据域
bTreeNode<T>* leftChild; //左孩子
bTreeNode<T>* rightChild; //右孩子
bTreeNode<T>* parent; //双亲结点
//函数成员
bTreeNode(); //无参的构造函数
bTreeNode(const T &d, bTreeNode<T>* Parent=NULL, bTreeNode<T>* LChild=NULL, bTreeNode<T>* RChild=NULL);
//已知元素、双亲、左右孩子
};
//无参的构造函数
template <class T>
bTreeNode<T>:: bTreeNode() //空结点
{
leftChild=NULL;
rightChild=NULL;
}
//含参的
template <class T>
bTreeNode<T>:: bTreeNode(const T &d, bTreeNode<T>* Parent=NULL, bTreeNode<T>* LChild=NULL, bTreeNode<T>* RChild=NULL)
{
data=d;
leftChild=LChild;
rightChild=RChild;
parent = Parent;
}
线索二叉树
定义
在某种遍历过后,二叉树的层序结构可以得到一个线性结构
【先看遍历】
leftChild | leftTag | Data | rightTag | rightChild |
- left/rightTag = 0
- leftChild:左孩子
- rightChild:右孩子
- left/rightTag = 1
- leftChild:前驱
- rightChild:后继
【有孩子就为孩子,没孩子就为前驱/后继】
【!!!前驱/后继 不一定是 双亲】