树和二叉树
树的定义
树:n(n≥0)个结点的有限集合。当n=0时,称为
空树;任意一棵非空树满足以下条件:
(1)有且仅有一个特定的称为根的结点;
⑵当n>1时,除根结点之外的其余结点被分成m (m>0)个互不相交的有限集合T1,T2,…
,Tm,其中每个集合又是一棵树,并称为这个根结点的子树。
树的基本术语
【1】结点的度:结点所拥有的子树的个数。
【2】树的度:树中各结点度的最大值。
【3】叶子结点:度为0的结点,也称为终端结点。
【4】分支结点:度不为0的结点,也称为非终端结点。
【5】孩子、双亲:树中某结点子树的根结点称为这个结点的孩子结点,这个结点称为它孩子结点的双亲结点;兄弟:具有同一个双亲的孩子结点互称为兄弟。
【6】路径:如果树的结点序列n1,
n2, …, nk有如下关系:结点ni是ni+1的双亲(1<=i<k),则把n1, n2, …, nk称为一条由n1至nk的路径;路径上经过的边的个数称为路径长度。
【7】祖先、子孙:在树中,如果有一条路径从结点x到结点y,那么x就称为y的祖先,而y称为x的子孙。
【8】结点所在层数:根结点的层数为1;对其余任何结点,若某结点在第k层,则其孩子结点在第k+1层。
【9】树的深度:树中所有结点的最大层数,也称高度。
【10】层序编号:将树中结点按照从上层到下层、同层从左到右的次序依次给他们编以从1开始的连续自然数。
【11】有序树、无序树:如果一棵树中结点的各子树从左到右是有次序的,称这棵树为有序树;反之,称为无序树。数据结构中讨论的一般都是有序树
【12】森林:m (m≥0)棵互不相交的树的集合。
树的遍历:
孩子兄弟表示法:
结点结构 firstchild data rightsib
data:数据域,存储该结点的数据信息; firstchild:指针域,指向该结点第一个孩子; rightsib:指针域,指向该结点的右兄弟结点。
template
struct TNode
{
T data;
TNode *firstchild, *rightsib; };
二叉树的定义:
二叉树是n(n≥0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。
二叉树的特点:
⑴ 每个结点最多有两棵子树;
⑵二叉树是有序的,其次序不能任意颠倒。
特殊的二叉树:
(1)斜树
1 .所有结点都只有左子
树的二叉树称为左斜树;
2 .所有结点都只有右子树的二叉树称为右斜树; 3.左斜树和右斜树统称为
斜树。
- 在斜树中,每一层只有一个结点;斜树的特点:
2.斜树的结点个数与其深度相同。
(2)满二叉树
在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子
都在同一层上。
满二叉树的特点:
-
叶子只能出现在最下一层;
-
只有度为0和度为2的结点。
完全二叉树:
对一棵具有n个结点的二叉树按层序编号,如果编号为i(1≤i≤n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中的位置完全相同。
完全二叉树的特点:
- 叶子结点只能出现在最下
两层,且最下层的叶子结点 H 都集中在二叉树的左部;
-
完全二叉树中如果有度为1的结点,只可能有一个,且该结点只有左孩子。
-
深度为k的完全二叉树在 k-1层上一定是满二叉树。
※二叉树的基本性质
性质5-1 二叉树的第i层上最多有2i-1个结点(i≥1)。
性质5-2
一棵深度为k的二叉树中,最多有2k-1个结点,最少有k个结点。
性质5-3
在一棵二叉树中,如果叶子结点数为n0,度为2的结点数为n2,则有: n0=n2+1。
性质5-4
具有n个结点的完全二叉树的深度为log2n +1。证明:假设具有n个结点的完全二叉树的深度为k,根据完全二叉树的定义和性质2,有下式成立
2k-1 ≤ n < 2k k所在区间对不等式取对数,有:
k-1≤log2n<k ⎣ log2n⎦ log2n ⎣ log2n ⎦ +1 log2n + 1 即:
log2n<k≤log2n+1
由于k是整数,故必有k=⎣ log2n ⎦ +1。
说明:符号⎣x⎦表示不大于x的最大整数。
性质5-5
对一棵具有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无右孩子。
二叉树的遍历操作:
前序遍历序列:A B D G C E F
中序遍历序列:D G B A E C F
后序遍历序列:G D B E F C A
层序遍历序列:A B C D E F G
二叉链表存储结构的类声明 :
void PreOrder(BiNode *root);
void InOrder(BiNode *root);
void PostOrder(BiNode *root);
void LevelOrder(BiNode *root);
private:
BiNode *root;
void
Creat(BiNode *root);
void Release(BiNode *root);
};
前序遍历——递归算法
template
void
BiTree::PreOrder(BiNode *root)
{
if
(root ==NULL) return;
else { cout<data;
PreOrder(root->lchild);
PreOrder(root->rchild);
}
}
中序遍历——递归算法
template
void BiTree::InOrder
(BiNode *root)
{ if (root==NULL) return;
else {
InOrder(root->lchild);
cout<data;
InOrder(root->rchild);
}
后序遍历——递归算法
template
void
BiTree::PostOrder(BiNode *root)
{
if (root==NULL) return;
else {
PostOrder(root->lchild);
PostOrder(root->rchild);
cout<data;
}
}
层序遍历
template
void
BiTree::LevelOrder(BiNode *root)
{const int MaxSize = 100; int front = 0;
int rear = 0; //采用顺序队列,并假定不会发生上溢
BiNode* Q[MaxSize];
BiNode* q; if (root==NULL) return; else{
Q[rear++] = root; while (front != rear)
{ q = Q[front++]; cout<data<<"
"; if (q->lchild != NULL)
Q[rear++] = q->lchild; if (q->rchild != NULL) Q[rear++] = q->rchild;
}
}
}
二叉树的建立
设二叉树中的结点均为一个字符。假设扩展二叉树的前序遍历序列由键盘输入,root为指向根结点的指针,二叉链表的建立过程是:
首先输入根结点,若输入的是一个“#”字符,则表明该二叉树为空树,即root=NULL;否则输入的字符应该赋给root->data,,之后依次递归建立它的左子树和右子树。
template
BiTree ::BiTree(BiNode
*root)
Creat(root);
建立二叉树递归算法 {({ )
}
template
void BiTree
::Creat(BiNode *root)
cin>>ch;
if (ch==’# ') root=NULL;
else {
root=new BiNode; root->data=ch;
Creat(root->lchild);
Creat(root->rchild);
}
}
template BiTree
::BiTree()
建立二叉树递归算法 {root =Creat ();
}
template BiNode*
BiTree ::Creat()
{
BiNode* root; T ch; cin>>ch; if (ch==’# ') root=NULL;
else {
root=new BiNode; root->data=ch;
root-> lchild=Creat(); root-> rchild=Creat();
} return root;
}
二叉树的销毁
二叉链表是动态存储分配,在析构函数中释放二叉链表的所有结点。
注意:先释放掉某结点的左、右子树才能释放这个结点。
所以,采用后序遍历,对结点的访问就是释放该结点。
template
BiTree::~BiTree(void)
{
Release(root); }
二叉树的销毁递归算法 {
template
void
BiTree::Release(BiNode* root)
if (root != NULL){
Release(root->lchild); //释放左子树 Release(root->rchild); //释放右子树 delete
root;
}
}
遍历二叉树是二叉树各种操作的基础,遍历算法中对每个结点的访问操作可以是多种形式及多个操作,根据遍历算法的框架,适当修改访问操作的内容,可以派生出很多关于二叉树的应用算法。
void
InOrder (BiNode *root)
{ if (root==NULL) return; else {
InOrder(root->lchild);
cout<data; InOrder(root->rchild);
}
}
设计算法求二叉树的结点个数。
int n=0; template void
Count(BiNode *root) //n为全局量并已初始化为0
{ if (root) {
Count(root->lchild); n++;
Count(root->rchild);
}
}
设计算法按前序次序打印二叉树中的叶子结点。
template void PreOrderleaf(BiNode
*root)
{ if (root) { if (!root->lchild
&& !root->rchild) cout<data;
PreOrderleaf(root->lchild);
PreOrderleaf(root->rchild); }
}
设计算法求二叉树的深度。
template
int Depth(BiNode *root)
{ if (root==NULL) return 0; else {
int hl=Depth(root->lchild); int
hr=Depth(root ->rchild); return hl>hr?hl+1:hr+1;
}
} 用孩子-兄弟表示法表示树,设计算法求树中结点 x 的第 i 个孩子。
用孩子-兄弟表示法表示树,设计算法求树中结点 x 的第 i 个孩子。