🍃树状关系
树(Tree)是n(n≥0)个节点的有限集合T,它满足两个条件 :
- 有且仅有一个特定的称为根(Root)的节点
- 其余的节点可以分为m(m≥0)个互不相交的有限集合T1、T2、……、Tm,其中每一个集合又是一棵树,并称为其根 的子树(Subtree)。
树有什么特征?
- 什么是度数:
一个节点的子树的个数称为该节点的度数,一棵树的度数是指该树中节点的最大度数
- 什么是边:什么是路径:
一个节点系列k1,k2, ……,ki,ki+1, ……,kj,并满足ki是ki+1的父节点,就称为一条从k1到kj的路径,路径的长度为j-1,即路径中的边数。
- 什么是高度(深度):
节点的层数等于父节点的层数加一,根节点的层数定义为一。树中节点层数的最大值称为该树的高度或深度。
什么是二叉树?
二叉树的定义 : 二叉树(Binary Tree)是n(n≥0)个节点的有限集合,它或者是空集(n=0),或者是由一个根节点以及两棵互不相交的、分别称为左子树和右子树的二叉树组成。二叉树与普通有序树不同,二叉树严格区分左孩子和右孩子,即使只有一个子节点也要区分左右。
🌿树状关系的顺序存储
顺序存储:
- 把待存储的树补成满二叉树
- 分配空间 2^k-1
- 存储:从上往下从左往右(层次遍历)
ABGCD/T//EF
缺点:
浪费空间
🌿树状关系的链式存储
不带头节点的操作
链式存储:
typedef int data_type;
typedef struct bintreenode
{
struct bintreenode *Left; //指向左孩子指针
data_type Data; //数据域
struct bintreenode *Right;//指向右孩子指针
}BTree;
不带头节点的链表操作
要插入时分为两个部分:
- 首次插入不需要额外申请空间
- 之后插入先申请空间再插入
🍀二叉树的遍历
☘️先序遍历
//函数功能:先序遍历
//函数参数:要遍历的树
//函数返回值:void
void Fir_Travel(BTree *pTmp)
{
if(!pTmp)
{
return;
}
//遍历根节点
printf("%d", pTmp->Data);
//以先序遍历的方式遍历左子树
Fir_Travel(pTmp->Left);
//以先序遍历的方式遍历右子树
Fir_Travel(pTmp->Right);
}
☘️中序遍历
//函数功能:中序遍历
//函数参数:要遍历的树
//函数返回值:void
void Mid_Travel(BTree *pTmp)
{
if(!pTmp)
{
return;
}
//以中序遍历的方式遍历左子树
Mid_Travel(pTmp->Left);
//遍历根节点
printf("%d", pTmp->Data);
//以先序遍历的方式遍历右子树
Mid_Travel(pTmp->Right);
}
☘️后序遍历
//函数功能:后序遍历
//函数参数:要遍历的树
//函数返回值:void
void Fin_Travel(BTree *pTmp)
{
if(!pTmp)
{
return;
}
//以后序遍历的方式遍历左子树
Fin_Travel(pTmp->Left);
//以后序遍历的方式遍历右子树
Fin_Travel(pTmp->Right);
//遍历根节点
printf("%d", pTmp->Data);
}
🍀排序二叉树
//函数功能:插入
//函数参数:被插入的树,要插入的值,是否首次插入
//函数返回值:成功返回OK,失败返回原因
int InsertItem(BTree *pTree, data_type item, int *Flag)
{
BTree *pNew = NULL;
BTree *pTmp = NULL;
//入参判断
if(!pTree)
{
return TREE_NULL;
}
//首次插入
if(*Flag)
{
//赋值
pTree->Data = item;
//清空
*Flag = 0;
return OK;
}
//非首次插入
pTmp = pTree;
//先申请空间
pNew = CreatNode();
//赋值
pNew->Data = item;
//判断
while(1)
{
if(pNew->Data < pTmp->Data)//往左边
{
if(!pTmp->Left)//当前位置能否插入
{
//插入
pTmp->Left = pNew;
retrun OK;
}
//往左边跑
pTmp = pTmp->Left;
}
else
{
if(!pTmp->Right)//当前位置能否插入
{
//插入
pTmp->Right = pNew;
retrun OK;
}
//往右边跑
pTmp = pTmp->Right;
}
}
}
访问;查询
遍历到的结点的Data和要查询的值进行对比
如果要查询的值 < pTmp->Data 在pTmp的左子树上找 pTmp->Left == NULL ×
如果要查询的值 = pTmp->Data √
如果要查询的值 > pTmp->Data 在pTmp的右子树上找 pTmp->Right == NULL ×
//函数功能:查询
//函数参数:查询的树,内容
//函数返回值:成功返回OK,失败返回原因
int Search(BTree *pTree, data_type item)
{
BTree *pTmp = NULL;
//入参判断
if(pTree == NULL)
{
return TREE_NULL;
}
pTmp = pTree;
while(1)
{
if(pTmp->Data == item)
{
puts("OK!");
return OK;
}
else if(pTmp->Data > item)
{
if(pTmp->Left == NULL)
{
puts("NO_SEARCH");
return NO_SEARCH;
}//往后找
pTmp = pTmp->Left;
}
else
{
if(pTmp->Right == NULL)
{
puts("NO_SEARCH");
return NO_SEARCH;
}//往后找
pTmp = pTmp->Right;
}
}
}
🍀删除节点
如果被删除结点没有子树,可以直接删除
如果被删除的结点有一个孩子,子承父位
如果被删除的结点有两个孩子,左子树中最右边的值(把左子树中最大的值放过来)右子树中最左边的值(把右子树中最小的值放过来)
🍀平衡二叉树
为了避免在某些情况下,树会降维成单链表,引入了平衡二叉树的概念
每一个结点,其子树的高度之差不超2 左右子树的高度之差 -1 0 1
🍀哈夫曼树
什么是赫夫曼树(最优二叉树):
是带权路径长度最短的树。
从树中一个结点到另外一个结点的分支构成一条路径,分支的数目称为路径的长度。
树的路径长度是指从树根到每个结点的路径长度之和。
结点的带权路径长度指的是从树根到该结点的路径长度和结点上权的乘积。
树的带权路径长度是指所有叶子节点的带权路径长度之和,记作 WPL 。
WPL最小的二叉树就是最优二叉树,又称为赫夫曼树