【数据结构】拆分详解 - 二叉树的链式存储结构
文章目录
- 一、前置说明
- 二、二叉树的遍历
- 1. 前序、中序以及后序遍历
- 1.1 前序遍历
- 1.2 中序遍历
- 1.3 后序遍历
- 2. 层序遍历
- 三、常见接口实现
- 0. 递归中的分治思想
- 1. 查找与节点个数
- 1.1 节点个数
- 1.2 叶子节点个数
- 1.3 第k层节点个数
- 1.4 查找值为x的节点
- 2. 二叉树的创建与销毁
- 2.1 创建
- 2.2 销毁
- 总结
一、前置说明
在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在大家对二叉树结构掌握还不够深入,为了降低大家学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式。
注意:下述代码并不是创建二叉树的方式,真正创建二叉树方式文章末尾处会进行讲解。
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType _data;
struct BinaryTreeNode* _left;
struct BinaryTreeNode* _right;
}BTNode;
BTNode* CreatBinaryTree()
{
BTNode* node1 = BuyNode(1);
BTNode* node2 = BuyNode(2);
BTNode* node3 = BuyNode(3);
BTNode * node4 = BuyNode(4);
BTNode* node5 = BuyNode(5);
BTNode* node6 = BuyNode(6);
node1->_left = node2;
node1->_right = node4;
node2->_left = node3;
node4->_left = node5;
node4->_right = node6;
return node1;
}
二、二叉树的遍历
1. 前序、中序以及后序遍历
学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的所有节点进行访问。
按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历,区别为访问根的顺序不同。
- 前序遍历 :访问顺序为根,左子树,右子树
- 中序遍历:访问顺序为左子树,根,右子树
- 后序遍历:访问顺序为左子树,右子树,根
1.1 前序遍历
void BinaryTreePrevOrder(BTNode* root)
{
//为空
if (root == NULL)
{
printf("N ");
return ;
}
//不为空,打印节点值,继续找左子树,找完后找右子树
printf("%d ", root->data);
BinaryTreePrevOrder(root->left);
BinaryTreePrevOrder(root->right);
}
1.2 中序遍历
void BinaryTreeInOrder(BTNode* root)
{
//为空
if (root == NULL)
{
printf("N ");
return;
}
//左
BinaryTreeInOrder(root->left);
//根
printf("%d ", root->data);
//右
BinaryTreeInOrder(root->right);
}
1.3 后序遍历
void BinaryTreePostOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
BinaryTreePostOrder(root->left);
BinaryTreePostOrder(root->right);
printf("%d ", root->data);
}
2. 层序遍历
除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。
三、常见接口实现
0. 递归中的分治思想
在下面的接口实现中我们使用递归实现分治思想(将大问题拆分成多个子问题处理),我们要注意如何拆分子问题 和 表达递归的结束条件。
- 递归:分为递推和回归。我们可以画递推展开图,假想递推到底的情况,思考分析回归条件,可代入回归检验是否符合。
- 分治:将问题抽象分为多个子问题,分别解决
- 用递归实现分治 :一路递推到底,处理完第一个子问题,回归,在回归过程中处理之前未处理的子问题。(子问题可能会被处理多次,但每个子问题的处理次数总和一定是相同的,只是处理先后的不同)本质是将问题的核心基础步骤抽象出来,使用递归方式重复处理,最终达成目的。
1. 查找与节点个数
1.1 节点个数
子问题分治:节点个数 = 左子树节点个数 + 右子树节点个数
递归(结束)返回条件:
- 空 返回 0
- 不为空 返回1
int BinaryTreeSize(BTNode* root)
{
//如果根为空就返回0,否则返回左子树与右子树节点数和
return root == NULL ? 0
: BinaryTreeSize(root->left)
+ BinaryTreeSize(root->right)
+ 1;
}
1.2 叶子节点个数
子问题分治:节点个数 = 左子树叶子节点个数 + 右子树叶子节点个数
递归(结束)返回条件:
- 空 返回 0
- 叶子 返回1
int BinaryTreeLeafSize(BTNode* root)
{
//单独判断空树
if (root == NULL)
{
return 0;
}
//如果左子树和右子树都不为空,说明为叶子
if (!root->left && !root->right)
{
return 1;
}
return BinaryTreeLeafSize(root->left)
+ BinaryTreeLeafSize(root->right);
}
1.3 第k层节点个数
子问题分治:第k层节点个数 = 第k-1层的左子树节点个数 + 第k-1层的右子树节点个数
递归(结束)返回条件:
- 空 返回 0
- 不为空且为k层 返回1
int BinaryTreeLevelKSize(BTNode* root, int k)
{
assert(k> 0);
if (root == NULL)
return 0;
//不为空且为k层,返回1
if (k == 1)
return 1;
//k == 1 说明为k层,不为则继续向下递推
return BinaryTreeLevelKSize(root->left, k-1)
+ BinaryTreeLevelKSize(root->right, k-1);
}
1.4 查找值为x的节点
子问题分治:查找 = 查找左子树 + 查找右子树
递归(结束)返回条件:
- 空 返回 0
- 找到 返回节点地址
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
return NULL;
if (root->data == x)
return root;
//使用指针变量存储地址,若不为空说明找到,不需要进行其他操作,使其一路回归
BTNode* ret1 = BinaryTreeFind(root->left, x);
if (ret1)
return ret1;
BTNode* ret2 = BinaryTreeFind(root->right, x);
if (ret2)
return ret2;
return NULL;
}
2. 二叉树的创建与销毁
2.1 创建
子问题分治:创建 = 创建左子树 + 创建右子树
递归(结束)返回条件:
- 空(‘#’) 返回NULL
- 非空 创建节点并初始化
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
// a为数组,pi为 外部中标识数组下标变量的 指针
BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{
if (a[*pi] == '#')
{
*pi++; //下标后移
return NULL;
}
BTNode* root = (BTNode*)malloc(sizeof(BTNode));
if (root == NULL)
{
perror("malloc fail");
exit(-1);
}
root->data = a[*pi];
root->left = BinaryTreeCreate(a, n, ++*pi);
root->right = BinaryTreeCreate(a, n, ++ * pi);
}
2.2 销毁
void BinaryTreeDestory(BTNode** root)
{
if (*root == NULL)
{
return;
}
BinaryTreeDestory((*root)->left);
BinaryTreeDestory((*root)->right);
free(*root);
*root == NULL;
}
总结
知识框架可看文章目录。
本文讲解了二叉树的链式存储结构的相关知识,递归和分治思想十分抽象,需要读者自行画递归展开图理解,多练习,培养出自己的抽象能力。
文章中有什么不对的丶可改正的丶可优化的地方,欢迎各位来评论区指点交流,博主看到后会一一回复。