在上一节中,我们说到了一种数据结构——【堆】。也看到了一些有关二叉树的基本雏形,在本节中,我们就来说说有关这种链式二叉树的具体实现以及应用
typedef int BTDataType;
typedef struct BinaryTreeNode {
BTDataType data;
struct BinaryTreeNode* lchild;
struct BinaryTreeNode* rchild;
}BTNode;
好,知道了链式二叉树的基本结构之后,我们就要去了解如何去遍历出这棵树。对于链式二叉树的遍历方式有四种,我们逐个来讲讲
规则:根——左子树——右子树
规则:左子树——根——右子树
规则:左子树——右子树——根
/*层序遍历*/
void LevelOrder(BTNode* root)
{
Qu q;
QueueInit(&q);
if (root != NULL) //首先将非空根结点入队
QueuePush(&q,root);
while (!QueueEmpty(&q))
{
QDataType front = QueueFront(&q); //首先获取队头结点
printf("%d ", front->data); //获取到之后输出打印
QueuePop(&q); //将其出队
//将其左右孩子结点再从队尾入队
if (front->lchild)
QueuePush(&q, front->lchild);
if(front->rchild)
QueuePush(&q, front->rchild);
}
QueueDestroy(&q);
}
刚才在初步了解了链式二叉树后学习了四种其遍历方式,但是遍历一棵链式二叉树并不是很难在二叉树这一节估计只有1到2分的难度,真正难的还在后面呢
int TreeNodeNum1(BTNode* root)
{
if (root == NULL)
return;
int sz = 0;
sz++;
TreeNodeNum1(root->lchild);
TreeNodeNum1(root->rchild);
return sz;
}
int TreeNodeNum1(BTNode* root)
{
if (root == NULL)
return;
static int sz = 0;
sz++;
TreeNodeNum1(root->lchild);
TreeNodeNum1(root->rchild);
return sz;
}
int sz = 0;
void TreeNodeNum1(BTNode* root)
{
if (root == NULL)
return;
sz++;
TreeNodeNum1(root->lchild);
TreeNodeNum1(root->rchild);
}
情景思路分析
这个听起来是很好理解,但是递归这种结构的话对于有些同学来说还是比较晦涩难懂的,因此我们通过一个特殊的情景来分析一下
疫情结束,马上就要返校复工了,此时学校需要统计有多多少人会返校,于是这件事情就交给副校长去办
假设每个领导最多只能管理两个人,现在呢副校长他想要了解到两个院系有多个人,那么他就要去询问两个副院长,也就是链式二叉树所对应的左右子树;那对于副院长呢,它想要知道这个院里有多少个人,那就需要找到每个班级对应的班主任去询问;然而对于班主任呢,班级方面的事情他就交给班长去负责,统计出班里有多少人返校
当班长搜集完信息后就汇报给班主任,班主任再汇报给副院长,最后副院长再汇报给副校长。副校长接收到一个副院长的统计汇报后再向另一个副院长去询问,返校人数信息,这对应的就是先访问完左子树之后再访问右子树,最后当另一个副院长也将人数汇报上来之后再加上他自己就是本次返校的人数了【当然这只是假设】
以下是代码
int TreeNodeNum(BTNode* root)
{
return root == NULL ? 0 :
TreeNodeNum(root->lchild) + TreeNodeNum(root->rchild) + 1;
}
以下是递归算法图解
int TreeLeafSize(BTNode* root)
{
if (root->lchild == NULL && root->rchild == NULL)
return 1; //若是左右孩子均为空,则表示只有结点
return TreeLeafSize(root->lchild) + TreeLeafSize(root->rchild);
}
这一块我们一起带VS来调试一下看看
int TreeLeafSize(BTNode* root)
{
if (root == NULL)
return 0;
if (root->lchild == NULL && root->rchild == NULL)
return 1; //若是左右孩子均为空,则表示只有结点
return TreeLeafSize(root->lchild) + TreeLeafSize(root->rchild);
}
规则:二叉树高度 = 左子树和右子树中高度大的那个 + 1
int TreeHeight(BTNode* root)
{
if (root == NULL)
return 0;
return TreeHeight(root->lchild) > TreeHeight(root->rchild) ?
TreeHeight(root->lchild) + 1 : TreeHeight(root->rchild) + 1;
}
int TreeHeight(BTNode* root)
{
if (root == NULL)
return 0;
int LHeight = TreeHeight(root->lchild);
int RHeight = TreeHeight(root->rchild);
return LHeight > RHeight ? LHeight + 1 : RHeight + 1; //返回左右子树中大的那一个再加上根结点
}
下面是算法图解
规则:
当k > 1 时,第k层的结点个数为其左孩子的第k - 1层 + 其右孩子的第k - 1层结点个数
当k == 1时,return 1
int TreeNodeNumK(BTNode* root, int k)
{
if (root == NULL)
return 0;
if (k == 1)
return 1;
return TreeNodeNumK(root->lchild, k - 1)
+ TreeNodeNumK(root->rchild, k - 1);
}
/*查找指定结点*/
BTNode* TreeFind(BTNode* root, BTDataType x)
{
if (!root)
return NULL;
if (root->data == x)
return root; //若根节点就是所要查找的结点,则直接返回
TreeFind(root->lchild, x);
TreeFind(root->rchild, x);
//没有理解清楚递归的返回过程
}
BTNode* TreeFind(BTNode* root, BTDataType x)
{
/*
* if (root == NULL)和if(!root)是等价的
* if(root) - 结点是存在的
* if(!root) - 结点是不存在的 --> 空的
* if (root == NULL) -->空的
* if (root != NULL) -->非空
*/
//if (root == NULL)
// return NULL;
if (!root)
return NULL;
if (root->data == x)
return root; //若根节点就是所要查找的结点,则直接返回
/*
* 要保存下当前查找的结点,否则递归回来没有接收返回的只会是一个空值或者随机值
*/
BTNode* ret1 = TreeFind(root->lchild, x);
if (ret1)
return ret1; //表明在左子树中找到了该结点,返回
BTNode* ret2 = TreeFind(root->rchild, x);
if (ret2)
return ret2; //表明在右子树中找到了该结点,返回
return NULL; //左右子树均没有找到,返回NULL
}
接下来,继续做一个进阶,在树和二叉树的基本概念中,我讲到了两种特殊的二叉树:满二叉树和完全二叉树,所以在这里我们要判断一棵树是否为完全二叉树,该如何去做呢?
若遇到的空结点后还有非空的结点,则表明其不为完全二叉树
若遇到的空结点后均为空,则表示其为完全二叉树
首先给出代码
/*判断二叉树是否为完全二叉树*/
bool TreeComplete(BTNode* root)
{
Qu qu;
QueueInit(&qu);
if (root)
QueuePush(&qu, root); //首选入队队头结点
while (!QueueEmpty(&qu))
{
QDataType front = QueueFront(&qu);
QueuePop(&qu);
//判断当前队列结点是否为空
if (front == NULL)
break; //若为空,则break跳出循环进行判断
else
{
//若不为空,则入队其左右孩子
QueuePush(&qu, front->lchild);
QueuePush(&qu, front->rchild);
}
}
//此处判断是否为完全二叉树
/*
* 1.若遇到的空结点后还有非空的结点,则表明若遇到的空结点后均为空,则表示其为完全二叉树其不为完全二叉树
* 2.
*/
if (!QueueEmpty(&qu))
{
QDataType front = QueueFront(&qu);
if (front != NULL)
{ //若是在队列中取到了非空的结点,则表示不为完全二叉树
QueueDestroy(&qu); //提前return要记得销毁队列
return false;
}
}
return true;
QueueDestroy(&qu);
}
注意:在判断出不为完全二叉树后提前【return false】了,就需要将队列销毁,否则会造成内存泄漏
/*销毁二叉树*/
void DestroyTree(BTNode* root)
{
if (root == NULL) //if(!root)
return;
DestroyTree(root->lchild);
DestroyTree(root->rchild);
free(root);
}
DestroyTree(n1);
n1 = NULL; //内部置空外部也要置空【形参改变不影响实参】
本模块请于我另一个专栏【LeetCode算法笔记】进行观看
也可直接点击此链接
学习完了链式二叉树,我们来总结一下所学习的知识点
以上就是本文所要描述的所有内容,感谢您对本文的观看,如有疑问请于评论区留言或者私信我都可以