文章目录
链式二叉树-----递归实现
链式二叉树的结构
定义
二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是
链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所 在的链结点的存储地址 。
递归
递归的定义
程序调用自身的编程技巧称为递归( recursion)。
递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,
递归策略
只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。
递归的主要思考方式在于:把大事化小
递归的必要条件
- 存在限制条件,当满足这个限制条件的时候,递归便不再继续。
- 每次递归调用之后越来越接近这个限制条件。
链式二叉树的实现
二叉树的创建和销毁
思路:链式二叉树的创建需满足链式二叉树的链式结构,即需定义一个结构体,数据域存放数据,一个指针域存放左孩子的结点地址,一个指针域存放右边兄弟的地址,每个结点都是这种结构。二叉树的销毁需采用递归的方式来销毁。
//创建二叉树及初始化二叉树
BTNode* CreatBinaryTree()
{
BTNode* nodeA = BuyNode('A');
BTNode* nodeB = BuyNode('B');
BTNode* nodeC = BuyNode('C');
BTNode* nodeD = BuyNode('D');
BTNode* nodeE = BuyNode('E');
BTNode* nodeF = BuyNode('F');
nodeA->left = nodeB;
nodeA->right = nodeC;
nodeB->left = nodeD;
nodeC->left = nodeE;
nodeC->right = nodeF;
//返回根节点
return nodeA;
}
//二叉树的销毁
void BinaryTreedestory(BTNode* root)
{
if (root==NULL)
{
return;
}
//采用后续遍历的方式来销毁二叉树
BinaryTreedestory(root->left);
BinaryTreedestory(root->right);
free(root);
}
二叉树的结点个数和叶子结点个数
思路:二叉树总的结点个数,就是二叉树的左子树的结点个数和右子树的结点个数之和,可以采用递归的方式将二叉树分为多个单个的结点,然后判断其是否为空,再计数。
叶子结点无子节点,或者说他的两个子节点都为NULL。根据这个条件,在求二叉树结点个数函数的基础上加以改进,都需要采用递归的方式。
//计数方法1:
void BinartSize(BTNode* root, int* pn)
{
if (root==NULL)
{
return;
}
//对计数器的地址进行解引用再加加
++(*pn);
BinartSize(root->left, pn);
BinartSize(root->right, pn);
}
//计数方法2:
int BinaryTreeSize(BTNode* root)
{
//若root为NULL,则表示该位置无结点,返回0,不为空,则表示该位置有结点,记1,然后通过递归累加起来
return root == NULL ? 0 : BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}
//叶子节点的个数
//叶子节点的左右子树都为NULL,故递归的条件可以设置为
//当一个节点的左右子树都为NULL时,返回1,
//总的叶子节点的个数 就等于根节点的左孩子的叶子节点数加右孩子的叶子节点数
int BinaryTreeLeafsize(BTNode* root)
{
//为NULL表示不是结点
if (root == NULL)
return 0;
//满足叶子结点的特征才能计数
if (root->left==NULL&&root->right==NULL)
{
return 1;
}
return BinaryTreeLeafsize(root->left) + BinaryTreeLeafsize(root->right);
}
二叉树第k层的结点个数
思路:例如: 求第三层的节点数,就转换为求A的左子树的第二层节点数 和 右子树的第二层的节点数之和,又可以转换成求 A的左子树 的左子树的第一层的节点数 和 右子树的第一层的节点数 之和,加上 A的右子树 的 左子树的第一层的节点数 和 右子树的第一层的节点数 之和。
//求第k层的节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
assert(k >= 1);
//root为NULL时,没有节点
if (root==NULL)
{
return 0;
}
//当程序走到这里时,就表示root不为空,这时k等于几就是第几层
//k等于1时,表示第一层,即根节点,有且只有一个节点,返回1
if (k==1)
{
return 1;
}
//当程序走到了这里,说明root不为空,且k!=1
//说明root这棵树的第k层的节点在子树里面,
// 但是在子树里面的第k层的节点数是未知的 第一层的节点数有且只有一个
//转换成求root这棵树的左右子树的第k-1层的节点数
return BinaryTreeLevelKSize(root->left, k - 1)+BinaryTreeLevelKSize(root->right,k-1);
}
求二叉树的深度h
思路:求树的深度,可以转换为求其左右子树的最大深度,两个比较,大的子树深度加一就是树的深度。
//求树的深度
//当前树的深度等于左子树和右子树比较,大的加一
int BinaryTreeDepth(BTNode* root)
{
if (root==NULL)
{
return 0;
}
//设置两个变量保存结果,防止返回时重复遍历
int leftdepth = BinaryTreeDepth(root->left);
int rightdepth = BinaryTreeDepth(root->right);
//当leftdepth 等于rightdepth 时,也就是访问到最后一个节点时,
//leftdepth ==0 rightdepth==0 返回值就是他俩随便一个加1返回
return leftdepth > rightdepth ? leftdepth + 1 : rightdepth + 1;
}
二叉树查找值为x的结点
思路:可以先从左子树遍历比较,若是左子树找到了,那就直接返回,不用再去遍历右子树,若没有在左子树找到,那就去右子树找,若还是没有找到,则返回NULL。
BTNode* BinaryTreeFind(BTNode* root, Datatype x)
{
if (root==NULL)
{
return NULL;
}
if (root->data==x)
{
//如果找到了,就直接返回
return root;
}
//程序走到这儿,说明还没找到,那就去根节点的左树去找
//若是找到了,则直接返回,没有找到返回NULL
//保存结果,防止重复遍历
BTNode* left = BinaryTreeFind(root->left, x);
if (left)
{
return left;
}
//左子树没找到,那就直接去右子树找,
//若是找到了,则直接返回,没有找到返回NULL
//保存结果,防止重复遍历
BTNode* right = BinaryTreeFind(root->right, x);
if (right)
{
return right;
}
//左右子树都没找到,说明该节点不存在
return NULL;
}
判断是否为完全二叉树
思路:完全二叉树的特征为:前N-1层都是满的,最后一层不满,且结点从左到右都是连续的。可以采用队列的方法,判断最后一个结点之前是否有结点为NULL,若有,则不是完全二叉树,否则就是完全二叉树。
//判断二叉树是否为完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
//创建队列并初始化
PQ q;
Queueinit(&q);
//树的根结点先入队
Queuepush(&q, root);
while (Queueempty(&q))
{
BTNode* front = Queuefront(&q);
Queuepop(&q);
//碰到NULL,直接跳出循环
if (front==NULL)
{
break;
}
else
{
Queuepush(&q, front->left);
Queuepush(&q, front->right);
}
}
//遇到空后,检查后面的节点
//若全为空,则是完全二叉树
//若存在非空,则不是完全二叉树
while (Queueempty(&q))
{
BTNode* front = Queuefront(&q);
Queuepop(&q);
if (front)
{
Queuedestroy(&q);
return false;
}
}
Queuedestroy(&q);
return true;
}
总结
二叉树这一节,即是重点也是难点,现在这点还只是基础,后续的学习中,还会学到AVL树,红黑树,树的遍历非递归实现,要是这点基础都没搞懂,那后面的学习就根本学不下去。这里二叉树的一些操作,主要是用到了函数递归的分治思想,将大问题分为一个个小问题去解决。递归的两个必要条件是:存在限制条件,且每次递归调用都越来越接近这个限制条件。还需多加练习,理解递归,二叉树的一些性质也极其重要!