二叉树
二叉树的逻辑结构
二叉树的定义
二叉树是n(n ≥ \ge ≥ 0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树的二叉树组成。
二叉树特点
1.每个结点最多有两颗子树。
2.二叉树是有序的,其次序不能任意颠倒。
注意:二叉树和树是两种树结构。
分类
了解好:斜树,满二叉树,完全二叉树
二叉树的基本性质
性质1:二叉树的第i层上最多有 2 i − 1 2^{i-1} 2i−1个结点(i ≥ \ge ≥ 1)
性质2:一棵深度为k的二叉树中,最多有 2 k − 1 2^{k-1} 2k−1个结点,最少有k个结点。
性质3:在一棵二叉树中,如果叶子结点数为 n 0 n_0 n0,度为2的结点数为 n 2 n_2 n2,则有: n 0 = n 2 + 1 n_0=n_2+1 n0=n2+1。
性质4:具有n个结点的完全二叉树的深度为 ⌊ log 2 n ⌋ + 1 \left\lfloor\log _{2} n\right\rfloor+1 ⌊log2n⌋+1。
性质5:对一棵具有n个结点的完全二叉树中从1开始按层序编号,则对于任意的序号为i(
1
≤
i
≤
n
1\le i \le n
1≤i≤n)的结点(简称为结点i),有:
(1)如果i>1,则结点的双亲结点的序号为i/2;如果i=1,则结点是根结点,无双亲结点。
(2)如果 2 i ≤ n 2i \le n 2i≤n,则结点i的左孩子的序号为2i;如果2i>n,则结点i无左孩子。
(3)如果 2 i + 1 ≤ n 2i+1\le n 2i+1≤n,则结点i的右孩子的序号为2i+1;如果 2 i + 1 > n 2i+1>n 2i+1>n,则结点无右孩子。
顺序存储结构
二叉树的顺序存储结构就是用一维数组存储二叉树中的结点,并且结点的存储位置(下标)应能体现结点之间的逻辑关系-父子关系。
完全二叉树的顺序储存
![R6CWa.png](https://i.imgtg.com/2022/11/07/R6CWa.png)
![R6IjK.png](https://i.imgtg.com/2022/11/07/R6IjK.png)
二叉树编号
按照完全二叉树编号
![R6XhB.png](https://i.imgtg.com/2022/11/07/R6XhB.png)
可以编号为:
![R66Ig.png](https://i.imgtg.com/2022/11/07/R66Ig.png)
没有编号的位置直接存储为空。
二叉链表
![R6wyS.png](https://i.imgtg.com/2022/11/07/R6wyS.png)
data | 数据域,存放该结点的数据信息 |
---|---|
Ichild | 左指针域,存放指针指向左孩子的指针 |
rchild | 右指针域,存放指针指向右孩子的指针 |
class tree
{
public:
int data;
class tree* Ichild;
class tree* rchild;
}
typedef class tree node;
typedef node *btree;
二叉树的遍历
二叉树的组成:根结点D,左子树L,右子树R。
如果限定先左后右,则二叉树遍历方式有三种:
前序(Preorder):DLR
中序(Inorder):LDR
后序(Postorder):LRD
如下图所视
![R8MsN.png](https://i.imgtg.com/2022/11/07/R8MsN.png)
中序遍历
1.遍历左子树
2.遍历(或访问)树根
3.遍历右子树
中序遍历为:FDHGIBEAC
void Inorder(btree ptr)
{
if (ptr != NULL)
{
Inorder(ptr->left); //遍历左子树
cout<<ptr ->data; //遍历并打印树根
Inorder(ptr->right); //遍历右子树
}
}
后序遍历
1.遍历左子树
2.遍历右子树
3.遍历树根
中序遍历为:FHIGDEBCA
void Postorder (btree ptr)
{
if (ptr != NULL)
{
Postorder(ptr->left); //遍历左子树
Postorder(ptr->right); //遍历右子树
cout<<ptr->data; //遍历并打印树根
}
前序遍历
1.遍历树根
2.遍历左子树
3.遍历右子树
中序遍历为:ABDFGHIEC
void Preorder(btree ptr)
{
if (ptr != NULL)
{
cout<<ptr ->data; //遍历并打印树根
Inorder(ptr->left); //遍历左子树
Inorder(ptr->right); //遍历右子树
}
}
二叉树节点的插入和删除
在二叉树建立的过程中,是根据左子树<树根<右子树的原则建立的。
查找
只需从树根出发比较键值,如果比树根大就往右,否则往左而下,直到相等就找到了要查找的值,如果比到NULL,无法再前进就代表查找不到此值。
btree search (btree ptr,int val) //查找二叉树某键值得函数
{
while(1)
{
if(ptr==NULL) //没找到就返回NULL
return NULL;
if(ptr->data==val) //节点值等于查找值
return ptr;
else if(ptr->data>val) //节点值大于查找值
ptr=ptr->left;
else //小于查找值
ptr=ptr->right;
}
}
插入操作
插入节点的情况和查找相似,关键是插入后仍要保持二叉查找树的特性(左子树<树根<右子树)。如果插入的节点在二叉树中没有找到,就是出现查找失败的情况,就相当于找到了要插入的位置。我们可以修改,只要多加一条if判断语句,当查找到键值时输出“二叉树中有此节点了!”,如果找不到,再将此节点加到此二叉树中。算法如下所示。
btree ptr=NULL;
if((search(ptr,data))!=NULL) //查找二叉树
cout<<"二叉树中有此节点了-"<<data<<endl;
else
{
ptr=creat_tree(ptr,data);//将此键值加入到此二叉树中
inorder(ptr);
}
btree creat_tree(btree root,int val)
{
btree newnode,current,backup;
newnode=(btree)malloc(sizeof(node));//创建一个新结点
newnode->data=val;
newnode->left=NULL;
newnode->right=NULL;
if(root==NULL) //如果树根为空
{
root=newnode;
return root;
}
else //树不为空
{
for(current=root;current!=NULL;) //把树赋给root
{
backup=current; //current赋给backup
if(current->data > val) //val小于此结点的data
current=current->left; //current被赋为右树的节点
else
current=current->right;//current被赋为左树的节点
}
if(backup->data >val) //节点数据域大于val值说明新结点是它的左孩子
backup->left=newnode;
else
backup->right=newnode;
}
return root;
}
}
void inorder(btree ptr) //中序遍历子程序
{
if(ptr!=NULL)
{
inorder(ptr->left);
cout<<"["<<ptr->data<<"]";
inorder(ptr->right);
}
}
二叉树的删除
1.删除的结点为树叶:只要将其相连的父节点指向NULL即可。
2.删除的节点只有一颗子树,如下图,要删除节点1,就要将其右指针放到其父节点的左指针
3.删除的节有两颗子树,如下图,要删除节点4,方式有两种
3.1 找出中序立即先行者(inorder immediate predecessor)
即是将欲删除节点的左子树中最大者向上提,在此即为图中的节点2,简单来说,就是在该节点的左子树,往右寻找,直到右指针为NULL,这个节点就是中序立即先行者。
3.2 找出中序立即后继者(inorder immediate successor)
即是将欲删除节点的右子树中最小者向上提,在此即为图中的节点5,简单来说,就是在该节点的右子树,往左寻找,直到左指针为NULL,这个节点就是中序立即后继者。
![R8aaC.png](https://i.imgtg.com/2022/11/07/R8aaC.png)