前言
二叉树在数据结构中比较重要的部分,属于逻辑结构。
包括:树和二叉树的转换,树转换成二叉树,森林转化尾二叉树的方法。
一、树是什么?
1.树是n(n>=0)个结点的有限集合,n=0时,它为空树。
2.树有且仅有一个根节点。
3.树除根节点以外,每一个结点可分为m(m>0)个互不相交的集合,其中每一个集合,又是一棵树。
二、树
1.树的基本术语
结点:树中的一个单独单元
结点的度:结点拥有的子树数目称为结点的度
树的度:树的度时树内各结点度的最大值
叶子: 度为0的结点或终端节点
非终端节点: 度不为0的结点
双亲和孩子: 结点的子树称为该结点的孩子,相应的,该节点称该孩子的双亲
兄弟: 拥有同一个双亲的结点之间互称为兄弟
祖先: 从根节点到该节点所经分支上的所有结点
子孙: 以某一结点为根的子树中任意结点称为该节点的子孙
层次: 结点的层数从根开始定义,根为第一层,根的孩子为第二层,以此类推。
堂兄弟:双亲在同一层的孩子结点称为堂兄弟
树的深度: 树节点中最大的层数称为树的深度或高度
有序树或无序树: 树中结点的各子树从左至右是有序的称为有序树,反之 ,为无序树。
森林: m(m>=0)棵树组成的集合。
2.二叉树
1. 二叉树是n(n>=0)个结点的有限集合,n=0时,它为空树。
2. 二叉树中每个结点的度不大于2
3. 二叉树的子树有左右之分
4. 有且仅有一个根结点
如果i处结点是二叉树的任意一结点,那么(2*i)处和(2*i+1)分别是它的左右孩子。
二叉树的性质
性质1:二叉树的第i层上最多有2^(i-1)个结点
性质2:深度为k的二叉树最多有2^k-1个结点
性质3:对于任意一棵二叉树,如果其终端结点(叶子节点)树为N0,度为2的结点数目为N2,则N0=N2+1
2.满二叉树(特殊的二叉树)
每一层上的节点数都是最大节点数,即每一层i的结点数都是2^(i-1)
深度为k的满二叉树有2^(k-1)个结点
3.完全二叉树(特殊的二叉树)
深度为k,且有n个结点,当且仅当其每一个节点都与深度为k的满二叉树中从1-n的编号一一对应。
在满二叉树的基础上最后一层的结点自右向左依次减少
叶子节点只能在层次最大的两层出现
对于任意一结点,若其右分支下的子孙最大层数为A,那么其左分支下的子孙最大层数必为A或A+1.
4.二叉树的存储结构
1.顺序存储结构
#define SIZEMAX 100
typedef TELemType SqBiTree[SiZEMAX];//1位置存储根节点信息
SqBiTree S;
以满二叉树为例
把根节点存放在S[1],那么它的左右孩子分别放在S[21]和S[21+1]处。
任意一结点在S[X],那么它的左右孩子分别放在S[2X]和S[2X+1]处。
2.链式存储结构
typedef struct Node
{
ElemType Data;//数据域
struct Node *Lchild;//左指针(左孩子)
struct Node *Rchild;//右指针(右孩子)
}BiTNode,*BiTree;
5.遍历二叉树
1.先序遍历(根左右)
2.中序遍历(左根右)
3.后序遍历(左右根)
以先序遍历为例(递归)
void Traverse(BiTree T)
{
if(T)
{
printf(T->Data);//每进入一个结点就先输出结点内容
Traverse(T->Lchild);
Traverse(T->Rchild);
}
}
递归遍历二叉树的实质就是反复压栈弹栈的过程。(以下面的二叉树为例进行分析)
先序遍历结果为 1 2 4 3.
非递归遍历二叉树就是利用栈反复进行压栈弹栈。先序和中序遍历类似,只有后续遍历有一点不同。
后序遍历要把每一个结点做一个标记,记录这个结点压栈的次数。
下面以后续遍历二叉树为例
typedef struct Node
{
ElemType Data;//数据域
struct Node *Lchild;//左指针(左孩子)
struct Node *Rchild;//右指针(右孩子)
int flag;//在创建二叉树的时候把flag的值赋予0
}BiTNode,*BiTree;
#define SIZE 10//栈的长度
//定义栈
typedef struct
{
BiTree *arr;//指针数组(存储BiTNode*类型的数据,简单来说就是存储BiTree类型的数据)
//Bitree会指向BiTNode类结点的地址。
int top;//标记
}Stack;
//初始化栈
void InitStack(Stack &S)
{
S.arr=new BiTree[SIZE];
if(!S.arr)
{
printf("申请内存失败\n");
}else
{
S.top=0;
printf("申请内存成功\n");
}
}
//压栈
void Push(Stack &S,BiTree P)
{
S.arr[S.top++]=P;
}
//弹栈
void Pop(Stack &S,BiTree &P)
{
P=S.arr[--S.top];
}
//判断栈是否为空
bool AdjustStack(Stack S)
{
if(S.top)
{
return true;
}else
{
return false;
}
}
//后续遍历二叉树
void Traverse(BiTree &T)//这里传入引用类型,是因为要修改每一个结点的flag标记
{
Stack S;//定义栈
BiTree Temp;
InitStack(S);//初始化栈
while(T||AdjustStack(S))
{
while(T)
{
Push(S,T);
T->flag++;//标记加一
T=T->Lchild;//指针向左孩子移动
}
if(AdjustStack(S))
{
Pop(S,Temp);
if(Temp->flag==2)
{
printf(Temp->data);//输出该节点的信息
}else
{
T=Temp->Rchild;
Push(S,T);
T—>flag++;
}
}
}
}
后续递归仅仅多了一个标记用来记录遍历指针经过该结点的次数。
6.线索二叉树
总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。