文章目录
上篇文章中,我们介绍了树形结构与树的相关概念,这篇文章我们讲讲二叉树。
二叉树
二叉树是一种特殊的树。二叉树的特点:
- 每个结点最多有两棵子树,即二叉树不存在度大于2的结点
- 二叉树的子树有左右之分,其子树的次序不能颠倒
- 二叉树即使只有一颗子树也要明确指出该子树是左子树还是右子树。
![](https://i-blog.csdnimg.cn/blog_migrate/c6f9ae6fa6215df963881b066cbf8233.png)
满二叉树&完全二叉树
-
满二叉树: 在一棵二叉树中,如果所有分支结点都存在左子树和右子树,并且所有叶子节点都在同一层上
-
完全二叉树:从根起,自上而下,自左而右,给满二叉树的每个结点从1到n连续编号,如果每个结点都与深度为k的满二叉树中编号从1至n的结点一一对应,则称为完全二叉树
![](https://i-blog.csdnimg.cn/blog_migrate/c29700365d56d91843691e5a303e3a55.png)
二叉树的性质(重要)
- 二叉树上叶子结点数等于度为2的节点数加1.
证明:
设在一棵二叉树中,n为所有节点总数,n0为叶子结点个数,n1为度为1的节点个数,n2为度为2的结点个数。则 n = n0 + n1 + n2 (1)
再看二叉树中的分支数:除根节点外,所有结点都有一个分支进入,设b为分支数,
则 n = b + 1
又因为分支是由度为1或2的结点分出的,因此:
b = n1 + 2 x n2
于是: n = n1 + 2 x n2 + 1 (2)
由(1)(2)得: n0 = n2 +1
- 二叉树上第i层上至少有2^(i-1)个结点。(i>=1,根结点的层数为1)
证明:数学归纳法
-
若规定只有根节点的二叉树的深度为1,则深度为K的二叉树的最大结点数是 2^k - 1 (k>=0)
-
对于完全二叉树(n≥1,n为节点数),如果按照
从上至下从左至右
的顺序对所有节点从1开始编号
,则对于序号为i的结点有:
4.1 除树的根节点外,若一个结点的编号为i,则双亲编号为:i/2。也就是说,当i为偶数的时候,其双亲结点编号为i/2,他是双亲节点的左孩子;当i为奇数的时候,其双亲结点编号为(i-1)/2,他是双亲节点的右孩子
4.2 如果 2 * i ≤ n,则编号为i的结点为分支结点;否则为叶子结点。
4.3 若n为奇数,则每个分支结点都既有左孩子又有右孩子;若n为偶数,则编号最大的分支结点只有左孩子,没有右孩子。
4.4 若编号为i的结点有左孩子,则左孩子结点编号为2i;若编号为i的结点有右孩子,则右孩子结点编号为2i+1
二叉树的存储结构
与线性表一样,二叉树也有顺序存储结构和链式存储结构。
1. 顺序存储结构
typedef DataType SqBinTree[MaxSize]; //其中DataType为二叉树结点的数据类型;MAxSize为顺序表的最大长度。
顺序存储一个完全二叉树,首先对该树的节点进行编号,然后以各结点的编号为下标,把各结点的值对应存储到一维数组中。完全二叉树的编号过程为:首先,把树的编号定为1,然后按照层次上从上到下、每层从左到右的顺序对每一结点进行编号。
对于一般二叉树,也可以用这种方法进行存储。首先,在二叉树上补上若干虚拟结点使其成为完全二叉树。然后按照上面的方法进行编号。
下表中是个二叉树顺序存储结构的示例:
![]() | ![]() |
顺序存储结构的
- 优点:存储完全二叉树,简单省空间 ;
- 缺点:存储一般二叉树尤其单支树,存储空间利用不高 。
2. 二叉链存储结构
链表中每个结点包含两个指针,分别对应结点的左孩子和右孩子。下面是代码实现:
struct BinTreeNode
{
struct BinTreeNode* _pLeft;
struct BinTreeNode* _pRight;
DataType _data;
};
这种存储结构的优点是访问结点的孩子很方便,有时为了方便访问结点的双亲可在每个节点中再增加一个指向双亲的指针域,就成了三叉链存储结构。
3. 三叉链存储结构
struct BinTreeNode
{
struct BinTreeNode* _pParent;
struct BinTreeNode* _pLeft;
struct BinTreeNode* _pRight;
DataType _data;
};
二叉树的基本操作(c++实现)
这里的二叉树采用二叉链存储结构。
1. 建立二叉链
用ch扫描采用括号表示法表示二叉树的字符串str。分为以下几种情况:
1)若 ch == ‘(’ ,则将前面刚创建的结点作为双亲节点进栈,并置k=1,表示其后创建的节点将作为这个节点的左孩子节点。
2)若 ch == ‘(’,表示栈中结点的左右孩子结点处理完毕,退栈。
3)若 ch == ‘,’,k=2,表示其后创建的节点为右孩子结点
4)若 ch 为一个字母,创建一个节点,并根据k值建立它与栈中结点之间的联系,当k=1表示这个节点作为栈中栈顶结点的左孩子结点;当k=2表示这个节点作为栈中栈顶结点的右孩子结点;如此循环直至str处理完毕。算法中使用一个栈st保存双亲节点,top为其栈顶指针;k指定其后处理的结点是双亲节点的左孩子结点还是右孩子。
#define MaxSize 20
typedef char DataType;
typedef struct tnode
{
DataType data;
struct tnode* pchild1;
struct tnode* pchild2;
}BTNode;
//建立二叉链
void CreateBTree(BTNode *&bt, char *str)
{
BTNode *st[MaxSize], *p = NULL;
int top = -1, k, j = 0;
char ch;
bt = NULL;//建立二叉树初始化为空
ch = str[j];
while ('\0' != ch) //str未扫描完时循环
{
switch (ch)
{
case '(':top++; st[top] = p; k = 1; break;//左孩子节点
case ')':top--; break;
case ',':k = 2; break; //右孩子结点
default:
p = (BTNode*)malloc(sizeof(BTNode));
p->data = ch;
p->pchild1 = p->pchild2 = NULL;
if (bt == NULL)
bt = p;
else
{
switch (k)
{
case 1:st[top]->pchild1 = p; break;
case 2:st[top]->pchild2 = p; break;
}
}
}
j++;
ch = str[j];
}
}
2. 求二叉树高度(递归求法)
int BTHeight(BTNode *bt)
{
int left, right;
if (NULL == bt)
{
return 0;
}
else
{
left = BTHeight(bt->pchild1);
right = BTHeight(bt->pchild2);
return (left > right) ? (left + 1) : (right + 1);
}
}
3. 求二叉树结点个数(递归算法)
int NodeCount(BTNode *bt)
{
int left, right;
if (NULL == bt)
{
return 0;
}
else
{
left = NodeCount(bt->pchild1);
right = NodeCount(bt->pchild2);
return left + right + 1;
}
}
4. 求二叉树叶子结点个数
int LeafCount(BTNode *bt)
{
int left, right;
if (NULL == bt)
return 0;
else if (NULL == bt->pchild1 && NULL == bt->pchild2)
return 1;
else
{
left = LeafCount(bt->pchild1);
right = LeafCount(bt->pchild2);
return left + right;
}
}
5. 以括号表示法输出二叉树
void DisBTree(BTNode *bt)
{
if (NULL != bt)
{
cout << bt->data;
if (NULL != bt->pchild1 || NULL != bt->pchild2)
{
cout << '(';
DisBTree(bt->pchild1);
if (NULL != bt->pchild2) cout << ",";
DisBTree(bt->pchild2);
cout << ")";
}
}
}
本篇结束,下篇文章介绍二叉树的遍历以及线索二叉树的代码实现。