数据结构(树和二叉树 Part2 树的创建):https://blog.csdn.net/qq_41605114/article/details/104690836
目录
4树和二叉树
4.1树的基本概念
树的定义:
由一个或多个(n>=0)结点组成的有限集合T,有且仅有一个结点称为根(root)
当n>1时,其余的结点分为m(m>=0)个互相不相交的有限集合
T1,T2,T3..........Tm。
每个集合本身又是棵数,被称作这个根的子数。
树的结构特点:
- 非线性结构,有一个直接前驱,但可能有多个直接后继(1:n)
- 树的定义具有递归性,树中还有树
- 树可以为空,即节点个数为零
相关术语:
- 根:即根节点(没有前驱)
- 叶子:即终端结点(没有后继)
- 森林:指m棵不相交的树的集合(例如删除A后的子数个数)
- 有序树:结点各子树从左到右有序,不能互换(左为第一)
- 无序书:结点各子树可互换位置
- 双亲:即上层的那个结点(直接前驱parent)
- 孩子:即下层结点的子树(直接后继child)
- 兄弟:同一双亲下的同层结点(孩子们之间互称兄弟)sibling
- 堂兄弟:即双亲位于同一层的结点(但并非同一双亲)cousin
- 祖先:即从根到该结点所经分支的所有结点
- 子孙:即该结点下层子树中的任一结点
- 结点:即树的数据元素
- 结点的度:结点挂接的子树的个数(有几个直接后继就是几度)
- 结点的层次:从根到该结点的层数(根结点算第一层)
- 终端结点:即度为0的结点,即叶子
- 分支结点:除树根以外的结点(也称为内部结点)
- 树的度:所有结点度中的最大值(Max(各结点的度))
- 树的深度(或高度):指所有结点中最大的层数(Max(各结点的层次))
如下图:
结点数:13
树的度:3
树的深度:4
4.2.1图形表示法
4.2.2广义表表示法
4.2.3左孩子右兄弟表示法
将上述多叉数转换为二叉树
顾名思义,左侧是孩子,右侧是孩子的兄弟
4.3二叉树(数据结构更好处理)
4.3.1二叉树基本概念
定义:
n(n>=0)个结点的有限集合,由一个根结点以及两颗互不相交(孩子个数不多于2)的,分别称为左子树和右子树的二叉树组成。
逻辑结构:
一对二(1:2)
基本特征:
每个结点最多只有两颗子树(不存在度大于2的结点)
左子树和右子树次序不能颠倒(有序数)
基本形态:
二叉树性质:
性质1:在二叉树的第i层上至多有2的i-1个结点(i>0)
性质2:深度为K的二叉树至多有2的K-1个结点(k>0)
性质3:对于任何一颗二叉树,若度为2的结点数有n2个,则叶子数(n0)必定为n2+1(n0 = n2 +1)
概念解释:
满二叉树
一颗深度为K且有2的k-1个结点的二叉树。
完全二叉树(数据结构堆——就是一种完全二叉树)
除最后一层外,每一层上的节点数均达到最大值;在最后一层上只缺少右边的若干结点。
理解:k-1层与满二叉树完全相同,第k层结点尽力靠左
性质4:具有n个结点的完全二叉树的深度 | log2n | + 1
性质5:对完全二叉树,若从上至下,从左至右编号(从1开始编号),则编号为i的结点,其左孩子编号必须是2i
其右孩子编号必须是2i+1,其双亲的编号必为i/2(i = 1时为根,除外)
如果不行,就把普通的树转换为完全二叉树
4.3.2二叉树的遍历
遍历定义
指按某条搜索路线遍访每个结点且不重复(又称周游)
遍历用途
它是树结构插入、删除、修改、查找和排序运算的前提,是二叉树一切运算的基础和核心
遍历方法
牢记一种约定:对每个结点的查看都是“先左后右”
限定先左后右,树的遍历有三种实现方案:
DLR:先(根)序遍历 先序遍历,即先根再左再右
LDR:中(根)序遍历 中序遍历,即先左再根再右
LRD:后(根)序遍历 后序遍历,即先左再左再根
注意:“先,中,后”的意思是指访问的结点D是先于子树出现还是后于子树出现。
从递归的角度看,这三种算法是完全相同的,或者说这三种遍历算法的访问路径是相同的
只是访问结点的时机不同。
以上图中的树作为实例
在访问的过程中,一旦在排序过程中碰见树,再次用排序原则进行排序。
程序部分 (二叉树)
以上图中的树为例,用程序进行以下一系列操作:
以上排序过程其实就是一个不断递归的过程
其递归调用的就是遍历的法则
先序
.h
struct BiNode
{
QString ch;
BiNode * lchild;//指向右孩子
BiNode * rchild;//指向左孩子
};
//二叉树的递归遍历
void recursion(BiNode * root);
.cpp
void recursion(BiNode * root)
{
if(nullptr == root)
return;
qDebug()<<"序号:"<<root->ch;
//递归遍历左子树
recursion(root->lchild);
//递归遍历右子树
recursion(root->rchild);
}
首先对输入的根节点进行判断,如果是空,直接return
因为是先序遍历,所以就先输出根节点的内容,之后再递归左右孩子
整个递归过程中,返回的标注就是:根结点为空,即返回,如此不断地递归
输出:
中序:
.cpp
//先序遍历
void recursion(BiNode * root)
{
if(nullptr == root)
return;
//递归遍历左子树
recursion(root->lchild);
qDebug()<<"序号:"<<root->ch;
//递归遍历右子树
recursion(root->rchild);
}
后序:
.cpp
void recursion(BiNode * root)
{
if(nullptr == root)
return;
//递归遍历左子树
recursion(root->lchild);
//递归遍历右子树
recursion(root->rchild);
qDebug()<<"序号:"<<root->ch;
}
二叉树叶子节点的个数计算
.cpp
void leafpointNum(BiNode * root)
{
if(nullptr == root)
return;
if(nullptr == root->lchild&&nullptr == root->rchild)
num++;
leafpointNum(root->lchild);
leafpointNum(root->rchild);
}
leafpointNum(&nodeA);
qDebug()<<"Num:"<<num;
输出:
计算树的高度
思路:看图,A的高度 = MAX(左子树高度,右子树高度) + 1(即自身高度);
//树的高度
int getTreeHigh(BiNode * root)
{
if(nullptr == root)
return 0;
int Lheight = getTreeHigh(root->lchild);
int Rheight = getTreeHigh(root->rchild);
int max = Lheight>Rheight ? Lheight+1:Rheight+1;
return max;
}
如此不断的递归,就得到了结果
qDebug()<<getTreeHigh(&nodeA);
树的拷贝
思路:先拷贝B,再拷贝F,最后拷贝A,将A的指针指向B和F
先拷贝孩子,再拷贝父亲,最后将父亲的指针指向孩子
//树的拷贝
BiNode * copyTree(BiNode * root)
{
if(nullptr == root)
return nullptr;
BiNode * Lcoyeresult = copyTree(root->lchild);
BiNode * Rcoyeresult = copyTree(root->rchild);
BiNode * node = new BiNode;
node->ch = root->ch;
node->lchild = Lcoyeresult;
node->rchild = Rcoyeresult;
return node;
}
操作:
BiNode nodeA = {"A",nullptr,nullptr};
BiNode nodeB = {"B",nullptr,nullptr};
BiNode nodeC = {"C",nullptr,nullptr};
BiNode nodeD = {"D",nullptr,nullptr};
BiNode nodeE = {"E",nullptr,nullptr};
BiNode nodeF = {"F",nullptr,nullptr};
BiNode nodeG = {"G",nullptr,nullptr};
BiNode nodeH = {"H",nullptr,nullptr};
nodeA.lchild = &nodeB;
nodeA.rchild = &nodeF;
nodeB.lchild = nullptr;
nodeB.rchild = &nodeC;
nodeC.lchild = &nodeD;
nodeC.rchild = &nodeE;
nodeF.lchild = nullptr;
nodeF.rchild = &nodeG;
nodeG.lchild = &nodeH;
nodeG.rchild = nullptr;
recursion(&nodeA);
leafpointNum(&nodeA);
qDebug()<<"Num:"<<num;
qDebug()<<getTreeHigh(&nodeA);
BiNode * nodecopyA = copyTree(&nodeA);
recursion(nodecopyA);
输出内容:
树的释放
从最底层,及叶子结点开始释放,同样是递归的思路
//树的释放
void FreeTree(BiNode * root)
{
if(nullptr == root)
return;
FreeTree(root->lchild);
FreeTree(root->rchild);
delete root;
}