📖 前言:本期我们将详解二叉树的链式存储结构,这也是生活中最常用的。
🎓 作者:HinsCoder
📦 作者的GitHub:代码仓库
📌 往期文章&专栏推荐:
目录📌
🕒 1. 二叉树的概念
🕘 1.1 二叉树链式存储结构
二叉树的链式结构可以存储任意树
(包括:普通二叉树,满二叉树,完全二叉树等),而二叉树的顺序结构(堆)存储普通二叉树会造成空间浪费。
二叉树可分两种:
- 空树
- 非空树:根节点、根节点的左子树、根节点的右子树组成
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
综上:二叉树定义是递归式
的,我们可以通过递归
的方式去遍历整个二叉树
🕒 2. 二叉树的遍历
🕘 2.1 前序遍历
前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
📪 简而言之:
访问顺序为:根——左子树——右子树
⚡ 注意
上述所说的根节点不仅仅指的是整棵树的根节点,也指每一个结点(因为每一个结点都可以当作一个根节点来看待)。即一棵完整的树,被拆分成多个子树看待。
ps:对函数递归展开不熟悉的同学可以像上图这样多画画递归展开图哦~
// 二叉树前序遍历
void PreOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%d ", root->data);
PreOrder(root->left);
PreOrder(root->right);
}
🕘 2.2 中序遍历
中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
📪 简而言之:
访问顺序为:左子树——根——右子树
// 二叉树中序遍历
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
🕘 2.3 后序遍历
后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。
📪 简而言之:
访问顺序为:左子树——右子树——根
// 二叉树后序遍历
void PostOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->data);
}
🕘 2.4 层序遍历
设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。
📪 简而言之:
访问顺序为:一层一层,从上到下,每层从左到右
🕒 3. 二叉树结点个数与高度等
🕘 3.1 二叉树结点个数
💡 思路1:静态变量法
int TreeSize(BTNode* root)
{
static int count = 0; //局部变量记得加static
if (root == NULL)
return count;
count++; //前序
TreeSize(root->left);
TreeSize(root->right);
return count;
}
⚡ 注意:不可取,因为如果再次调用就会出现问题
💡 思路2:遍历计数法
// 遍历计数
int count = 0; //全局变量
void TreeSize2(BTNode* root)
{
if (root == NULL)
return;
count++;
TreeSize2(root->left);
TreeSize2(root->right);
return;
}
⚡ 注意:每次调用前都要置为0,比较麻烦
💡 思路3:划分子问题
int TreeSize(BTNode* root)
{
return root == NULL ? 0 :
TreeSize(root->left) + TreeSize(root->right) + 1;//左+右+自己
}
🕘 3.2 二叉树叶子结点个数
int TreeLeafSize(BTNode* root)
{
if (root == NULL)
return 0;
if (root->left == NULL
&& root->right == NULL)
return 1;
return TreeLeafSize(root->left)
+ TreeLeafSize(root->right);
}
🕘 3.3 二叉树高度/深度
int TreeHeight(BTNode* root)
{
if (root == NULL)
return 0;
int lh = TreeHeight(root->left);
int rh = TreeHeight(root->right);
return lh > rh ? lh + 1 : rh + 1;
}
🕘 3.4 二叉树第K层结点个数
// 第K层节点个数
int TreeKLevelSize(BTNode* root, int k)
{
assert(k > 0);
if (root == NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
// 转换成求子树第k-1层
return TreeKLevelSize(root->left, k - 1)
+ TreeKLevelSize(root->right, k - 1);
}
🕘 3.5 查找二叉树,并返回所在结点
思考一下,下面的写法对吗❓
BTNode* TreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
{
return NULL;
}
if (root->data == x)
{
return root;
}
TreeFind(root->left, x);
TreeFind(root->right, x);
}
这种是不对的,详见图解
正确方法:
// 返回x所在的结点
BTNode* TreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
{
return NULL;
}
if (root->data == x)
{
return root;
}
// 先去左树找
BTNode* lret = TreeFind(root->left, x);
if (lret)
return lret;
// 左树没有找到,再去右树找
BTNode* rret = TreeFind(root->right, x);
if (rret)
return rret;
return NULL;
}
🕘 3.6 修改二叉树结点
💡 思路:配合查找函数使用
BTNode* ret = TreeFind(root, 5);
ret->data *= 10;
🕒 4. 二叉树相关OJ题
详见后续更新~
OK,以上就是本期知识点“链式二叉树”的知识啦~~ ,感谢友友们的阅读。后续还会继续更新,欢迎持续关注哟📌~
🎉如果觉得收获满满,可以点点赞👍支持一下哟~