1、定义
二叉树(Binary Tree)是n(n>=0)个结点的有限集合,该集合或者为空集,或者由一个根节点和两棵互不相关的、分别称为左子树和右子树的二叉树组成。
2、二叉树的特点
a、每个结点最多有两棵子树,所以二叉树不存在度大于1的结点
b、左子树和右子树是有顺序的,次序不能任意颠倒
c、即使某个结点只有一棵子树,也要区分是左子树还是右子树
3、二叉树的基本形态
1、定义
二叉树(Binary Tree)是n(n>=0)个结点的有限集合,该集合或者为空集,或者由一个根节点和两棵互不相关的、分别称为左子树和右子树的二叉树组成。
2、二叉树的特点
a、每个结点最多有两棵子树,所以二叉树不存在度大于1的结点
b、左子树和右子树是有顺序的,次序不能任意颠倒
c、即使某个结点只有一棵子树,也要区分是左子树还是右子树
3、二叉树的基本形态
4、二叉树的性质
- 在二叉树的第i层最多有2i-1个结点(i>=1)
- 深度为k的二叉树至多有2k-1个结点(k>=1)
- 任何一棵二叉树,其叶子节点与度为2的结点之间的关系是n0=n2+1
- 具有n个结点的完全二叉树的深度为[log2n]+1([]表示向下取整)
- 一棵有n个结点的完全二叉树,对任一结点i有:
如果i=1,则结点i为二叉树的根节点,否则其双亲的结点为[i/2]
如果2i>n,则结点i无左孩子,否则其左孩子是结点2i
如果2i+1>n,则结点i无右孩子,否则其右孩子为2i+1
5、二叉树的存储
- 顺序存储结构—一维数组
下标: | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| A | B | C | Ʌ | E | F | Ʌ | Ʌ | J |
Ʌ表示不存在的结点
- 二叉链表
lchild | data | rchild |
data为数据域,lchild和rchild为指针域,分别指向左右孩子指针
二叉链表的结构定义代码:
//二叉树的二叉链表结点结构定义
typedef int TElemType; //树结点的数据类型,目前暂定为整型
typedef struct BiTNode //结点结构
{
TElemType data;
struct BiTNode *rchild, *lchild;
} BiTNode, *BiTree;
6、二叉树的遍历
把树中的结点变成某种意义上的线性序列
前序遍历 中序遍历 后序遍历 层序遍历
//前序遍历递归算法 先双亲再左再右
void PreOrderTraverse(BiTree T)
{
if(T == NULL)
return;
printf("%c", T->data); //显示结点数据,可以更改为对结点的其他操作,例如修改
PreOrderTraverse(T->lchild); //再遍历左子树
PreOrderTraverse(T->rchild); //再遍历右子树
}
//中序遍历递归算法 先左再双亲再右
void InOrderTraverse(BiTree T)
{
if(T == NULL)
return;
InOrderTraverse(T->lchild); //先中序遍历左子树
printf("%c", T->data); //显示结点数据,可以更改为对结点的其他操作,例如修改
InOrderTraverse(T->rchild); //再中序遍历右子树
}
//后序遍历递归算法 先左再右最后双亲
void PostOrderTraverse(BiTree T)
{
if(T == NULL)
return;
PostOrderTraverse(T->lchild); //先后序遍历左子树
PostOrderTraverse(T->rchild); //再后序遍历右子树
printf("%c", T->data); //显示结点数据,可以更改为对结点的其他操作,例如修改
}
由上述的代码可知,遍历二叉树根据不同的顺序遍历时,只是调节数据结点的数据和遍历左右子树三者的先后顺序,其实现的基本原理都是递归。
7、遍历二叉树的性质:
A、已知前序和中序遍历序列,可以唯一确定一棵二叉树
//已知前序和中序确定一棵二叉树
class Solution {
public:
BiTree* reConstructBinaryTree(vector<int> pre,vector<int> vin) {
//输入合法性判断,不能为空,先序和中序的长度要一致
if(pre.empty()|| in.empty()|| pre.size()!= in.size()) {
return NULL;
}
//在前序遍历pre中查找根节点,并确定其在in的位置
size_t index=0;
for(;index <=in.size(); index++){
if(in[index]==pre[0]){ //在in中找到根节点时退出,此时index就是其在in中的位置
break;
}
}
//创建新的根节点,并为其赋值
BiTree* node=new BiTree(pre[0]);
//根据中序遍历将根节点左右两侧一分为二,根节点的左侧为左子树,右侧为右子树
vector<int> pl,pr;
vector<int> vl,vr;
//将前序、中序中根节点的左右子树记录下来
for(size_t j=0; j<index; j++){
pl.push_back(pre[j+1]);
vl.push_back(in[j]);
}
for(size_t j=index+1; j<in.size(); j++){
pr.push_back(pre[j]);
vr.push_back(in[j]);
}
//递归调用构建当前节点的左子树
node->left=reConstructBinaryTree(pl,vl);
//递归调用构建当前节点的右子树
node->right=reConstructBinaryTree(pr,vr);
return BiTNode;
}
};
B、已知后序和中序遍历序列,可以唯一确定一棵二叉树
//已知后序和中序确定一棵二叉树
class Solution {
public:
BiTree* reConstructBinaryTree(vector<int> post,vector<int> vin) {
//输入合法性判断,不能为空,后序序和中序的长度要一致
if(post.empty()|| in.empty()|| post.size()!= in.size()) {
return NULL;
}
//在前序遍历post中查找根节点,并确定其在in的位置
size_t index=0;
size_t plen = post.size();
size_t ilen = in.size();
for(;index <=ilen; index++){
if(in[index]==post[plen-1]){ //在in中找到根节点时退出,此时index就是其在in中的位置
break;
}
}
//创建新的根节点,并为其赋值
BiTree* node=new BiTree(post[plen-1]);
//根据中序遍历将根节点左右两侧一分为二,根节点的左侧为左子树,右侧为右子树
vector<int> pl,pr;
vector<int> il,ir;
//将后序、中序中根节点的左右子树记录下来
for(size_t j=0; j<index; j++){
pl.push_back(post[j]);
vl.push_back(in[j]);
}
for(size_t j=index+1; j<ilen; j++){
pr.push_back(post[j]);
vr.push_back(in[j]);
}
//递归调用构建当前节点的左子树
node->left=reConstructBinaryTree(pl,il);
//递归调用构建当前节点的右子树
node->right=reConstructBinaryTree(pr,ir);
return BiTNode;
}
};
C、已知前序和后序不能确定一棵二叉树
例如已知前序为ABC,后序为CBA,可能结果有
8、二叉树的建立
//按前序输入二叉树中结点的值(字符)构造二叉树链表表示二叉树T,#表示空树
void CreatBiTree(BiTree T)
{
TElemType ch;
scanf("%c",&ch);
if(ch == '#');
*T = NULL;
else{
*T = (BiTree)malloc(sizeof(BiTNode)); //分配一个结点的空间
if(!*T)
exit(OVERFLOW); //分配失败
(*T)->data = ch; //生成根节点
CreatBiTree(&(*T)->lchild); //构造左子树
CreatBiTree(&(*T)->rchild); //构造右子树
}
}
参考资料:《大话数据结构》