⭐二叉树 Binary Tree (BT)⭐
♾️ 简介 Introduction
二叉树(BT)是一种重要的树形数据结构类型,许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。二叉树特点是每个节点最多只能有两棵子树,且有左右之分。
二叉树是
n
n
n个有限元素的集合,该集合或者为空、或者由一个称为根(root)
的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成,是有序树。当集合为空时,称该二叉树为空二叉树。在二叉树中,一个元素也称作一个节点。
⭐目录可以看右侧⭐
点击功能栏第一个就可以在右侧看到目录了
⭐ 术语
name | english name | introduction |
---|---|---|
节点 | n o d e node node | 包含一个数据元素及若干指向子树分支的信息。 |
节点的度 | n o d e ′ s node's node′s d e g r e e degree degree | 一个节点拥有子树的数目称为节点的度。 |
叶子节点 | l e a f leaf leaf n o d e node node | 也称为终端节点,没有子树的节点或者度为零的节点。 |
分支节点 | b r a n c h branch branch n o d e node node | 也称为非终端节点,度不为零的节点称为非终端节点。 |
树的度 | D e g r e e Degree Degree o f of of t r e e tree tree | 树中所有节点的度的最大值。 |
节点的层次 | H i e r a r c h y Hierarchy Hierarchy o f of of n o d e s nodes nodes | 从根节点开始,假设根节点为第1层,根节点的子节点为第2层,依此类推,如果某一个节点位于第L层,则其子节点位于第 L + 1 L+1 L+1层。 |
树的深度 | T r e e Tree Tree d e p t h depth depth | 也称为树的高度,树中所有节点的层次最大值称为树的深度。 |
有序树 | O r d e r e d T r e e OrderedTree OrderedTree | 如果树中各棵子树的次序是有先后次序,则称该树为有序树。 |
无序树 | U n o d e r e d T r e e UnoderedTree UnoderedTree | 如果树中各棵子树的次序没有先后次序,则称该树为无序树。 |
森林 | F o r e s t Forest Forest | 由 m ( m ≥ 0 ) m(m≥0) m(m≥0)棵互不相交的树构成一片森林。如果把一棵非空的树的根节点删除,则该树就变成了一片森林,森林中的树由原来根节点的各棵子树构成。 |
⭐ 二叉树的形态
二叉树是递归定义的,其节点有左右子树之分,逻辑上二叉树有5种基本形态。
- 空二叉树
- 只有一个节点的二叉树
- 只有左子树
- 只有右子树
- 完全二叉树:深度为 k k k,有 n n n个节点的二叉树当且仅当其每一个节点都与深度为 k k k的满二叉树中编号从1到n的节点一一对应时,称为完全二叉树。也就是说 空位都在右边。
- 满二叉树:如果一棵二叉树只有度为0的节点和度为2的节点,并且度为0的节点在同一层上,则这棵二叉树为满二叉树
完全二叉树的特点是叶子节点只可能出现在层序最大的两层上,并且某个节点的左分支下子孙的最大层序与右分支下子孙的最大层序相等或大1
⭐ 二叉树的性质
- 二叉树的第 i i i 层上至多有 2 i − 1 ( i ≥ 1 ) 2^{i-1}(i≥1) 2i−1(i≥1) 个节点。
- 深度为 h h h的二叉树中至多含有 2 h − 1 2h-1 2h−1 个节点。
- 若在任意一棵二叉树中,有 n 0 n0 n0个叶子节点,有 n 2 n2 n2个度为2的节点,则必有 n 0 = n 2 + 1 n0=n2+1 n0=n2+1。
- 具有 n n n个节点的满二叉树深为 log 2 n + 1 \log_{2}{n}+1 log2n+1。
- 若对一棵有
n
n
n 个节点的完全二叉树进行顺序编号
(
1
≤
i
≤
n
)
(1≤i≤n)
(1≤i≤n),那么,对于编号为
i
(
i
≥
1
)
i(i≥1)
i(i≥1) 的节点:
- 当 i = 1 i=1 i=1时,该节点为根,它无双亲节点。
- 当 i > 1 i>1 i>1时,该节点的双亲节点的编号为 i / 2 i/2 i/2。
- 若 2 i ≤ n 2i≤n 2i≤n,则有编号为 2 i 2i 2i的左节点,否则没有左节点
- 若 2 i + 1 ≤ n 2i+1≤n 2i+1≤n,则有编号为 2 i + 1 2i+1 2i+1的右节点,否则没有右节点。
⭐C语言实现二叉树 Binart Tree (BT)
❄️ 构建一个二叉树
我们需要定义 一个结构体,里面有我们存放的数据 和两个指向这个 结构体类型的指针。
这两个指针分别指向 左子树 和 右子树。
♾️定义结构体
// 节点的结构体
typedef struct Node {
// 数据
int data = 0;
// 指向左节点的指针
struct Node *leftNode;
// 指向右节点的指针
struct Node *rightNode;
} Tree, *Fork ;
/**
* @brief 创建一个新节点
**/
Fork creat(int data) {
// 申请一个节点的内存
Fork node = (Fork)malloc(sizeof(Node)) ;
// 这个节点的左右节点都是空
node->rightNode = NULL;
node->leftNode = NULL;
// 这个节点的数据等于传入数据
node->data = data;
// 返回这个节点的地址
return node;
}
❄️ 给二叉树添加一个节点(数据)
一个二叉树 可以在度不为 2 的节点上添加一个新的节点。
我们编写一个函数 需要知道 根节点 节点内的数据 新节点相对于根节点所在的位置
♾️函数
/**
* @brief 给树添加一个节点
*
* @param root 树的根节点
* @param data 数据
* @param position 添加的位置 l 左 r 右 如 "lrrl"
*
* @return 添加是否成功 1 成功 0 失败
**/
bool add_node( Fork root, int data, char * position ) {
if ( position[1] == '\0' ) {
// 如果这个字符已经是结尾了 那么就在这个位置的之叶添加
if ( position[0] == 'r' ) {
// 如果是在右节点插入
if ( root->rightNode == NULL ) {
// 如果右节点是空的话就代表可以添加新的节点
root->rightNode = creat(data);
return 1;
} else {
return 0;
}
} else if (position[0] == 'l') {
// 如果是在左节点插入
if ( root->leftNode == NULL ) {
// 如果左节点是空的话就代表可以添加新的节点
root->leftNode = creat(data);
return 1;
} else {
return 0;
}
} else {
return 0;
}
} else {
// 这个字符不是结尾
if ( position[0] == 'r' ) {
// 如果是在右节点插入
if ( root->rightNode == NULL ) {
// 如果右节点是空的话就代表插入路径有问题
return 0;
} else {
// 如果右节点是不是空的话就迭代下一步
return add_node(root->rightNode,data,&position[1]);
}
} else if (position[0] == 'l') {
// 如果是在左节点插入
if ( root->leftNode == NULL ) {
return 0;
} else {
return add_node(root->leftNode,data,&position[1]);
}
} else {
return 0;
}
}
}
❄️ 给二叉树插入一个数据(数据)
实际上和 给二叉树添加一个节点(数据) 类似 只是多了一步 需要把原位置的树添加到现在这个节点下。
♾️函数
/**
* @brief 创建一个新节点
**/
Fork creat_node(int data, Fork leftNode, Fork rightNode) {
// 申请一个节点的内存
Fork node = (Fork)malloc(sizeof(Node)) ;
// 这个节点的左右节点
node->rightNode = rightNode;
node->leftNode = leftNode;
// 这个节点的数据等于传入数据
node->data = data;
// 返回这个节点的地址
return node;
}
/**
* @brief 插入一个节点
*
* @param root 根节点
* @param data 数据
* @param position 插入的位置 l 左 r 右 如 "lrrl"
* @param child 插入位置树 在 新节点的 哪个子叶上
*
* @return 插入是否成功 1 成功 0 失败
**/
bool insert_node( Fork root, int data, char * position, char child) {
if ( position[1] == '\0' ) {
// 如果这个字符已经是结尾了 那么就在这个位置的子叶添加
if ( position[0] == 'r' ) {
// 如果是在右节点插入
if ( child == 'r' ) {
// 如果后续树在右边的话
root->rightNode = creat_node(data, NULL, root->rightNode);
return 1;
} else if (child == 'l') {
// 如果后续树在左边的话
root->rightNode = creat_node(data, root->rightNode, NULL);
return 1;
} else {
return 0;
}
} else if (position[0] == 'l') {
// 如果是在左节点插入
if ( child == 'r' ) {
// 如果后续树在右边的话
root->leftNode = creat_node(data, NULL, root->leftNode);
return 1;
} else if (child == 'l') {
// 如果后续树在左边的话
root->leftNode = creat_node(data, root->leftNode, NULL);
return 1;
} else {
return 0;
}
} else {
return 0;
}
} else {
// 这个字符不是结尾
if ( position[0] == 'r' ) {
// 如果是在右节点插入
if ( root->rightNode == NULL ) {
// 如果右节点是空的话就代表插入路径有问题
return 0;
} else {
// 如果右节点是不是空的话就迭代下一步
return insert_node(root->rightNode, data, &position[1], child);
}
} else if (position[0] == 'l') {
// 如果是在左节点插入
if ( root->leftNode == NULL ) {
return 0;
} else {
return insert_node(root->leftNode, data, &position[1], child);
}
} else {
return 0;
}
}
}
❄️ 二叉树取出子树
二叉树取出子树
♾️函数实现
/**
* @brief 取出子树
*
* @param root 主树
* @param position 子树父节点相对于主树的位置
* @param child 子树在父节点的那个子叶
*
* @return 子树
**/
Fork takeout_tree( Fork root, char * position, char child ) {
if ( position[0] == '\0' ) {
// 如果这个字符已经到结尾了 那么就在这个位置取出
// 如果是在右节点去除
if ( child == 'r' ) {
// 如果取出树右边的话
Fork takeout = root->rightNode;
root->rightNode = NULL;
return takeout;
} else if (child == 'l') {
// 如果取出树左边的话
Fork takeout = root->leftNode;
root->leftNode = NULL;
return takeout;
} else {
return 0;
}
} else {
// 这个字符不是结尾
if ( position[0] == 'r' ) {
// 如果是在右节点插入
if ( root->rightNode == NULL ) {
// 如果右节点是空的话就代表插入路径有问题
return 0;
} else {
// 如果右节点是不是空的话就迭代下一步
return takeout_tree(root->rightNode, &position[1], child);
}
} else if (position[0] == 'l') {
// 如果是在左节点插入
if ( root->leftNode == NULL ) {
return 0;
} else {
return takeout_tree(root->leftNode, &position[1], child);
}
} else {
return 0;
}
}
}
❄️ 二叉树添加子树
给二叉树度小于2的节点添加一个子二叉树。
♾️函数实现
/**
* @brief 给二叉树度小于2的节点添加一个子二叉树。
*
* @param root
* @param position
* @param child
* @param tree
*
* @return
**/
bool add_node(Fork root, char * position, char child, Fork tree) {
if ( position[0] == '\0' ) {
// 如果这个字符已经到结尾了 那么就在这个位置添加
if ( child == 'r' ) {
// 如果在右节点添加的话
if ( root->rightNode == NULL ) {
// 右节点是空的话就添加
root->rightNode = tree;
return 1;
} else {
return 0;
}
} else if (child == 'l') {
// 如果在右节点添加的话
if ( root->leftNode == NULL ) {
// 右节点是空的话就添加
root->leftNode = tree;
return 1;
} else {
return 0;
}
} else {
return 0;
}
} else {
// 这个字符不是结尾
if ( position[0] == 'r' ) {
// 如果是在右节点插入
if ( root->rightNode == NULL ) {
// 如果右节点是空的话就代表插入路径有问题
return 0;
} else {
// 如果右节点是不是空的话就迭代下一步
return add_node(root->rightNode, &position[1], child, tree);
}
} else if (position[0] == 'l') {
// 如果是在左节点插入
if ( root->leftNode == NULL ) {
return 0;
} else {
return add_node(root->leftNode, &position[1], child, tree);
}
} else {
return 0;
}
}
}
❄️ 二叉树删除节点
二叉树删除节点比较复杂,需要分多种情况:
- 删除度为 0 的节点。(删除子叶)
- 删除度为 1 的节点。
- 删除度为 2 的节点。
♾️删除子叶
/**
* @brief 删除子叶
*
* @param root 根节点
* @param position 删除的子叶父节点的位置
* @param child 删除的节点是父节点的哪个孩子
*
* @return 是否成功
**/
bool del_node_0(Fork root, char * position, char child) {
if ( position[0] == '\0' ) {
// 如果这个字符已经到结尾了 那么就在这个位置添加
if ( child == 'r' ) {
// 如果在右节点添加的话
if ( root->rightNode == NULL ) {
// 右节点是空的话就不删除
return 0;
} else {
// 判断这个节点的度
int degree = 0;
if ( root->rightNode->leftNode != NULL ) degree++;
if ( root->rightNode->rightNode != NULL ) degree++;
if ( degree == 0 ) {
// 删除
free(root->rightNode);
root->rightNode = NULL;
return 1;
} else {
return 0;
}
}
} else if (child == 'l') {
// 如果在左节点删除的话
if ( root->leftNode == NULL ) {
// 左节点是空的话就不删除
return 0;
} else {
// 判断这个节点的度
int degree = 0;
if ( root->leftNode->leftNode != NULL ) degree++;
if ( root->leftNode->rightNode != NULL ) degree++;
if ( degree == 0 ) {
// 删除
free(root->leftNode);
root->leftNode = NULL;
return 1;
} else {
return 0;
}
}
} else {
return 0;
}
} else {
// 这个字符不是结尾
if ( position[0] == 'r' ) {
// 如果是在右节点删除
if ( root->rightNode == NULL ) {
// 如果右节点是空的话就代表插入路径有问题
return 0;
} else {
// 如果右节点是不是空的话就迭代下一步
return del_node_0(root->rightNode, &position[1], child);
}
} else if (position[0] == 'l') {
// 如果是在左节点删除
if ( root->leftNode == NULL ) {
return 0;
} else {
return del_node_0(root->leftNode, &position[1], child);
}
} else {
return 0;
}
}
}
♾️删除度为 1 的节点 (删除度小于2的节点)
/**
* @brief 删除度小于 2 的节点
*
* @param root 根节点
* @param position 删除的子叶父节点的位置
* @param child 删除的节点是父节点的哪个孩子
*
* @return 是否成功
**/
bool del_node_1(Fork root, char * position, char child) {
if ( position[0] == '\0' ) {
// 如果这个字符已经到结尾了 那么就在这个位置添加
if ( child == 'r' ) {
// 如果在右节点添加的话
if ( root->rightNode == NULL ) {
// 右节点是空的话就不删除
return 0;
} else {
// 判断这个节点的度
int degree = 0;
Fork childTree = NULL;
if ( root->rightNode->leftNode != NULL ) {
degree++ ;
childTree = root->rightNode->leftNode;
}
if ( root->rightNode->rightNode != NULL ) {
degree++ ;
childTree = root->rightNode->rightNode;
}
if ( degree < 2 ) {
// 删除
free(root->rightNode);
root->rightNode = childTree;
return 1;
} else {
return 0;
}
}
} else if (child == 'l') {
// 如果在左节点删除的话
if ( root->leftNode == NULL ) {
// 左节点是空的话就不删除
return 0;
} else {
// 判断这个节点的度
int degree = 0;
Fork childTree = NULL;
if ( root->leftNode->leftNode != NULL ) {
degree++ ;
childTree = root->leftNode->leftNode;
}
if ( root->leftNode->rightNode != NULL ) {
degree++ ;
childTree = root->leftNode->rightNode;
}
if ( degree < 2 ) {
// 删除
free(root->leftNode);
root->leftNode = childTree;
return 1;
} else {
return 0;
}
}
} else {
return 0;
}
} else {
// 这个字符不是结尾
if ( position[0] == 'r' ) {
// 如果是在右节点删除
if ( root->rightNode == NULL ) {
// 如果右节点是空的话就代表插入路径有问题
return 0;
} else {
// 如果右节点是不是空的话就迭代下一步
return del_node_0(root->rightNode, &position[1], child);
}
} else if (position[0] == 'l') {
// 如果是在左节点删除
if ( root->leftNode == NULL ) {
return 0;
} else {
return del_node_0(root->leftNode, &position[1], child);
}
} else {
return 0;
}
}
}
♾️删除度为 2 的节点
/**
* @brief 删除度为2的节点
*
* @param root 根节点
* @param position 要删除节点的父节点相对于根节点的位置
* @param child 要删除节点是父节点的哪个孩子
* @param inheritChild 取代父节点的孩子
* @param insertPosition 另一个孩子的父节点在取代原父节点的哪个位置
* @param insertChild 另一个孩子是新父节点的哪个孩子
*
* @return 是否成功
**/
bool del_node_2(Fork root, char * position, char child, char inheritChild, char * insertPosition, char insertChild ) {
if ( position[0] == '\0' ) {
// 如果这个字符已经到结尾了 那么就在这个位置添加
if ( child == 'r' ) {
// 如果在右节点删除的话
if ( root->rightNode == NULL ) {
// 右节点是空的话就不删除
return 0;
} else {
// 取出 要删除节点的左右节点
Fork leftChildTree = takeout_tree( root, (char*)"r", 'l' );
Fork rightChildTree = takeout_tree( root, (char*)"r", 'r' );
if ( inheritChild == 'l' ) {
// 如果左树为取代树的话 先把右树植入左树
if ( add_node(leftChildTree, insertPosition, insertChild, rightChildTree) ) {
// 右树植入成功的话植入删除节点并植入左树
del_node_0(root, position, child );
add_node(root, position, child, leftChildTree);
return 1;
} else {
// 失败的话复原
add_node(root, (char*)"r", 'l', leftChildTree);
add_node(root, (char*)"r", 'r', rightChildTree);
return 0;
}
} else if ( inheritChild == 'r' ) {
// 如果先植入右树的话
// 如果先植入右树的话 先把左树植入左树
if ( add_node(rightChildTree, insertPosition, insertChild, leftChildTree) ) {
// 左树植入成功的话植入 删除节点并植入右树
del_node_0(root, position, child );
add_node(root, position, child, rightChildTree);
return 1;
} else {
// 失败的话复原
add_node(root, (char*)"r", 'l', leftChildTree);
add_node(root, (char*)"r", 'r', rightChildTree);
return 0;
}
} else {
return 0;
}
}
} else if (child == 'l') {
// 如果在左节点添加的话
if ( root->leftNode == NULL ) {
// 左节点是空的话就不删除
return 0;
} else {
// 取出 要删除节点的左右节点
Fork leftChildTree = takeout_tree( root, (char*)"l", 'l' );
Fork rightChildTree = takeout_tree( root, (char*)"l", 'r' );
if ( inheritChild == 'l' ) {
// 如果左树为取代树的话 先把右树植入左树
if ( add_node(leftChildTree, insertPosition, insertChild, rightChildTree) ) {
// 右树植入成功的话植入删除节点并植入左树
del_node_0(root, position, child );
add_node(root, position, child, leftChildTree);
return 1;
} else {
// 失败的话复原
add_node(root, (char*)"l", 'l', leftChildTree);
add_node(root, (char*)"l", 'r', rightChildTree);
return 0;
}
} else if ( inheritChild == 'r' ) {
// 如果先植入右树的话
// 如果先植入右树的话 先把左树植入左树
if ( add_node(rightChildTree, insertPosition, insertChild, leftChildTree) ) {
// 左树植入成功的话植入 删除节点并植入右树
del_node_0(root, position, child );
add_node(root, position, child, rightChildTree);
return 1;
} else {
// 失败的话复原
add_node(root, (char*)"l", 'l', leftChildTree);
add_node(root, (char*)"l", 'r', rightChildTree);
return 0;
}
} else {
return 0;
}
}
} else {
return 0;
}
} else {
// 这个字符不是结尾
if ( position[0] == 'r' ) {
// 如果是在右节点删除
if ( root->rightNode == NULL ) {
// 如果右节点是空的话就代表插入路径有问题
return 0;
} else {
// 如果右节点是不是空的话就迭代下一步
return del_node_2(root->rightNode, &position[1], child, inheritChild, insertPosition, insertChild );
}
} else if (position[0] == 'l') {
// 如果是在左节点删除
if ( root->leftNode == NULL ) {
return 0;
} else {
return del_node_2(root->leftNode, &position[1], child, inheritChild, insertPosition, insertChild );
}
} else {
return 0;
}
}
}
❄️ 二叉树的遍历
二叉树的遍历方法主要有4种,分别为:前序遍历
、中序遍历
以及后序遍历
再加一种层序遍历
。
♾️前序遍历
前序遍历:root -> left -> right
用三个字描述就是 父左右
void pre_order(Fork root)
{
if (root == NULL)
{
printf("%c \n", '*');
return;
}
printf("id - > %d name -> %s \n", root->id, root->name);
pre_order(root->leftNode);
pre_order(root->rightNode);
}
♾️中序遍历
中序遍历:left -> root -> right
用三个字描述就是 左父右
/**
* @brief 中序遍历
*
* @param root
**/
void ino_order(Fork root)
{
if (root == NULL)
{
printf("%c \n", '*');
return;
}
ino_order(root->leftNode);
printf("id - > %d name -> %s \n", root->id, root->name);
ino_order(root->rightNode);
}
♾️ 后序遍历
后序遍历: left -> right -> root
用三个字描述就是 左右父
/**
* @brief 后序遍历
*
* @param root
**/
void pos_order(Fork root)
{
if (root == NULL)
{
printf("%c \n", '*');
return;
}
pos_order(root->leftNode);
pos_order(root->rightNode);
printf("id - > %d name -> %s \n", root->id, root->name);
}
♾️层序遍历
层序遍历相比于前三种遍历,会更加难一点点。
由上至下,从左到右的方式对这个树进行编号。
让他根据编号大小成为一个线性表。
//层序遍历
void level_order(Fork root) {
queue <Fork> q;
if ( root == NULL ) {
return ;
} else {
q.push(root);
while (!q.empty()) {
Fork node = q.front();
printf("id -> %d name -> %s \n", node->id, node->name);
q.pop();
if ( node->leftNode != NULL ) {
q.push( node->leftNode);
}
if ( node->rightNode != NULL) {
q.push( node->rightNode);
}
}
}
}
♾️例程 ♾️
#include <iostream>
#include <queue>
using namespace std;
// 节点的结构体
typedef struct Node {
// 数据
int data = 0;
// 指向左节点的指针
struct Node *leftNode;
// 指向右节点的指针
struct Node *rightNode;
} Tree, *Fork ;
/****************************************************
* 函数的定义 *
***************************************************/
Fork creat(int data); // 创建新节点的函数
// 增
bool add_node( Fork root, int data, char * position ); // 添加数据函数
bool insert_node( Fork root, int data, char * position, char child); // 插入数据函数
Fork takeout_tree( Fork root, char * position, char child ); // 取出节点函数
bool add_node(Fork root, char * position, char child, Fork tree); // 添加节点函数
// 删
bool del_node_0(Fork root, char * position, char child); // 删除子叶函数
bool del_node_1(Fork root, char * position, char child); // 删除度为 1 的节点
bool del_node_2(Fork root, char * position, char child, char inheritChild, char * insertPosition, char insertChild ); // 删除度为2的节点
// 遍历
void pre_order(Fork root); // 前序遍历
void ino_order(Fork root); // 中序遍历
void pos_order(Fork root); // 后序遍历
void level_order(Fork root); // 层序遍历
int main() {
// 创建一个新节点 这个节点就是这个树的根节点
Fork root = creat(2);
printf("\nCreated a new binart tree begin 2 now!\n");
// 在左子叶添加新节点
printf("\n在左子叶添加新节点\n");
add_node(root, 7, (char*)"l");
printf("Add a node 7 \n");
printf("root -> %d \nroot-left -> %d\n", root->data, root->leftNode->data);
// 在左子树插入新节点
printf("\n在左子树插入新节点\n");
insert_node(root, 8, (char*)"l", 'l');
printf("Insert a node 8 \n");
printf("root -> %d \nroot-left -> %d\nroot-left-left -> %d \n", root->data, root->leftNode->data, root->leftNode->leftNode->data );
// 提取出 根节点的左子树
printf("\n提取出 根节点的左子树\n");
Fork takeout = takeout_tree(root, (char*)"", 'l');
printf("takeout->%d\n", takeout->data);
printf("takeout->leftNode->%d\n", takeout->leftNode->data);
// 把提取出的子树 作为 根节点的右子树
printf("\n把提取出的子树 作为 根节点的右子树\n");
add_node( root, (char*)"", 'r', takeout );
printf("root -> %d \nroot-right -> %d\nroot-right-left -> %d \n", root->data, root->rightNode->data, root->rightNode->leftNode->data );
// 添加一个节点 到 右子树的右子叶上
printf("\n添加一个节点 到 右子树的右子叶上\n");
add_node(root, 12, (char*)"rr");
printf("root -> %d \nroot-right -> %d\nroot-right-left -> %d \nroot-right-right -> %d \n", root->data, root->rightNode->data, root->rightNode->leftNode->data, root->rightNode->rightNode->data );
// 删除根节点的右节点 并且 被删除的节点的右节点取代被删除的节点 被删除的节点的左节点在取代的节点的左边
printf("\n删除根节点的右节点 并且 被删除的节点的右节点取代被删除的节点 被删除的节点的左节点 在取代的节点的左边\n");
del_node_2(root, (char*)"", 'r', 'r', (char*)"", 'l' );
printf("root -> %d \nroot-right -> %d \nroot-right-left -> %d \n", root->data, root->rightNode->data, root->rightNode->leftNode->data );
// 前序遍历
printf("\n前序遍历\n");
pre_order(root);
}
/**
* @brief 创建一个新节点
**/
Fork creat(int data) {
// 申请一个节点的内存
Fork node = (Fork)malloc(sizeof(Node)) ;
// 这个节点的左右节点都是空
node->rightNode = NULL;
node->leftNode = NULL;
// 这个节点的数据等于传入数据
node->data = data;
// 返回这个节点的地址
return node;
}
/**
* @brief 创建一个新节点
**/
Fork creat_node(int data, Fork leftNode, Fork rightNode) {
// 申请一个节点的内存
Fork node = (Fork)malloc(sizeof(Node)) ;
// 这个节点的左右节点
node->rightNode = rightNode;
node->leftNode = leftNode;
// 这个节点的数据等于传入数据
node->data = data;
// 返回这个节点的地址
return node;
}
/**
* @brief 给树添加一个节点
*
* @param root 树的根节点
* @param data 数据
* @param position 添加的位置 l 左 r 右 如 "lrrl"
*
* @return 添加是否成功 1 成功 0 失败
**/
bool add_node( Fork root, int data, char * position ) {
if ( position[1] == '\0' ) {
// 如果这个字符已经是结尾了 那么就在这个位置的之叶添加
if ( position[0] == 'r' ) {
// 如果是在右节点插入
if ( root->rightNode == NULL ) {
// 如果右节点是空的话就代表可以添加新的节点
root->rightNode = creat(data);
return 1;
} else {
return 0;
}
} else if (position[0] == 'l') {
// 如果是在左节点插入
if ( root->leftNode == NULL ) {
// 如果左节点是空的话就代表可以添加新的节点
root->leftNode = creat(data);
return 1;
} else {
return 0;
}
} else {
return 0;
}
} else {
// 这个字符不是结尾
if ( position[0] == 'r' ) {
// 如果是在右节点插入
if ( root->rightNode == NULL ) {
// 如果右节点是空的话就代表插入路径有问题
return 0;
} else {
// 如果右节点是不是空的话就迭代下一步
return add_node(root->rightNode, data, &position[1]);
}
} else if (position[0] == 'l') {
// 如果是在左节点插入
if ( root->leftNode == NULL ) {
return 0;
} else {
return add_node(root->leftNode, data, &position[1]);
}
} else {
return 0;
}
}
}
/**
* @brief 插入一个节点
*
* @param root 根节点
* @param data 数据
* @param position 插入的位置 l 左 r 右 如 "lrrl"
* @param child 插入位置树 在 新节点的 哪个子叶上
*
* @return 插入是否成功 1 成功 0 失败
**/
bool insert_node( Fork root, int data, char * position, char child) {
if ( position[1] == '\0' ) {
// 如果这个字符已经是结尾了 那么就在这个位置的子叶添加
if ( position[0] == 'r' ) {
// 如果是在右节点插入
if ( child == 'r' ) {
// 如果后续树在右边的话
root->rightNode = creat_node(data, NULL, root->rightNode);
return 1;
} else if (child == 'l') {
// 如果后续树在左边的话
root->rightNode = creat_node(data, root->rightNode, NULL);
return 1;
} else {
return 0;
}
} else if (position[0] == 'l') {
// 如果是在左节点插入
if ( child == 'r' ) {
// 如果后续树在右边的话
root->leftNode = creat_node(data, NULL, root->leftNode);
return 1;
} else if (child == 'l') {
// 如果后续树在左边的话
root->leftNode = creat_node(data, root->leftNode, NULL);
return 1;
} else {
return 0;
}
} else {
return 0;
}
} else {
// 这个字符不是结尾
if ( position[0] == 'r' ) {
// 如果是在右节点插入
if ( root->rightNode == NULL ) {
// 如果右节点是空的话就代表插入路径有问题
return 0;
} else {
// 如果右节点是不是空的话就迭代下一步
return insert_node(root->rightNode, data, &position[1], child);
}
} else if (position[0] == 'l') {
// 如果是在左节点插入
if ( root->leftNode == NULL ) {
return 0;
} else {
return insert_node(root->leftNode, data, &position[1], child);
}
} else {
return 0;
}
}
}
/**
* @brief 取出子树
*
* @param root 主树
* @param position 子树父节点相对于主树的位置
* @param child 子树在父节点的那个子叶
*
* @return 子树
**/
Fork takeout_tree( Fork root, char * position, char child ) {
if ( position[0] == '\0' ) {
// 如果这个字符已经到结尾了 那么就在这个位置取出
// 如果是在右节点去除
if ( child == 'r' ) {
// 如果取出树右边的话
Fork takeout = root->rightNode;
root->rightNode = NULL;
return takeout;
} else if (child == 'l') {
// 如果取出树左边的话
Fork takeout = root->leftNode;
root->leftNode = NULL;
return takeout;
} else {
return 0;
}
} else {
// 这个字符不是结尾
if ( position[0] == 'r' ) {
// 如果是在右节点插入
if ( root->rightNode == NULL ) {
// 如果右节点是空的话就代表插入路径有问题
return 0;
} else {
// 如果右节点是不是空的话就迭代下一步
return takeout_tree(root->rightNode, &position[1], child);
}
} else if (position[0] == 'l') {
// 如果是在左节点插入
if ( root->leftNode == NULL ) {
return 0;
} else {
return takeout_tree(root->leftNode, &position[1], child);
}
} else {
return 0;
}
}
}
/**
* @brief 给二叉树度小于2的节点添加一个子二叉树。
*
* @param root
* @param position
* @param child
* @param tree
*
* @return
**/
bool add_node(Fork root, char * position, char child, Fork tree) {
if ( position[0] == '\0' ) {
// 如果这个字符已经到结尾了 那么就在这个位置添加
if ( child == 'r' ) {
// 如果在右节点添加的话
if ( root->rightNode == NULL ) {
// 右节点是空的话就添加
root->rightNode = tree;
return 1;
} else {
return 0;
}
} else if (child == 'l') {
// 如果在右节点添加的话
if ( root->leftNode == NULL ) {
// 右节点是空的话就添加
root->leftNode = tree;
return 1;
} else {
return 0;
}
} else {
return 0;
}
} else {
// 这个字符不是结尾
if ( position[0] == 'r' ) {
// 如果是在右节点插入
if ( root->rightNode == NULL ) {
// 如果右节点是空的话就代表插入路径有问题
return 0;
} else {
// 如果右节点是不是空的话就迭代下一步
return add_node(root->rightNode, &position[1], child, tree);
}
} else if (position[0] == 'l') {
// 如果是在左节点插入
if ( root->leftNode == NULL ) {
return 0;
} else {
return add_node(root->leftNode, &position[1], child, tree);
}
} else {
return 0;
}
}
}
/**
* @brief 删除子叶
*
* @param root 根节点
* @param position 删除的子叶父节点的位置
* @param child 删除的节点是父节点的哪个孩子
*
* @return 是否成功
**/
bool del_node_0(Fork root, char * position, char child) {
if ( position[0] == '\0' ) {
// 如果这个字符已经到结尾了 那么就在这个位置添加
if ( child == 'r' ) {
// 如果在右节点添加的话
if ( root->rightNode == NULL ) {
// 右节点是空的话就不删除
return 0;
} else {
// 判断这个节点的度
int degree = 0;
if ( root->rightNode->leftNode != NULL ) degree++;
if ( root->rightNode->rightNode != NULL ) degree++;
if ( degree == 0 ) {
// 删除
free(root->rightNode);
root->rightNode = NULL;
return 1;
} else {
return 0;
}
}
} else if (child == 'l') {
// 如果在左节点删除的话
if ( root->leftNode == NULL ) {
// 左节点是空的话就不删除
return 0;
} else {
// 判断这个节点的度
int degree = 0;
if ( root->leftNode->leftNode != NULL ) degree++;
if ( root->leftNode->rightNode != NULL ) degree++;
if ( degree == 0 ) {
// 删除
free(root->leftNode);
root->leftNode = NULL;
return 1;
} else {
return 0;
}
}
} else {
return 0;
}
} else {
// 这个字符不是结尾
if ( position[0] == 'r' ) {
// 如果是在右节点删除
if ( root->rightNode == NULL ) {
// 如果右节点是空的话就代表插入路径有问题
return 0;
} else {
// 如果右节点是不是空的话就迭代下一步
return del_node_0(root->rightNode, &position[1], child);
}
} else if (position[0] == 'l') {
// 如果是在左节点删除
if ( root->leftNode == NULL ) {
return 0;
} else {
return del_node_0(root->leftNode, &position[1], child);
}
} else {
return 0;
}
}
}
/**
* @brief 删除度小于 2 的节点
*
* @param root 根节点
* @param position 删除的子叶父节点的位置
* @param child 删除的节点是父节点的哪个孩子
*
* @return 是否成功
**/
bool del_node_1(Fork root, char * position, char child) {
if ( position[0] == '\0' ) {
// 如果这个字符已经到结尾了 那么就在这个位置添加
if ( child == 'r' ) {
// 如果在右节点添加的话
if ( root->rightNode == NULL ) {
// 右节点是空的话就不删除
return 0;
} else {
// 判断这个节点的度
int degree = 0;
Fork childTree = NULL;
if ( root->rightNode->leftNode != NULL ) {
degree++ ;
childTree = root->rightNode->leftNode;
}
if ( root->rightNode->rightNode != NULL ) {
degree++ ;
childTree = root->rightNode->rightNode;
}
if ( degree < 2 ) {
// 删除
free(root->rightNode);
root->rightNode = childTree;
return 1;
} else {
return 0;
}
}
} else if (child == 'l') {
// 如果在左节点删除的话
if ( root->leftNode == NULL ) {
// 左节点是空的话就不删除
return 0;
} else {
// 判断这个节点的度
int degree = 0;
Fork childTree = NULL;
if ( root->leftNode->leftNode != NULL ) {
degree++ ;
childTree = root->leftNode->leftNode;
}
if ( root->leftNode->rightNode != NULL ) {
degree++ ;
childTree = root->leftNode->rightNode;
}
if ( degree < 2 ) {
// 删除
free(root->leftNode);
root->leftNode = childTree;
return 1;
} else {
return 0;
}
}
} else {
return 0;
}
} else {
// 这个字符不是结尾
if ( position[0] == 'r' ) {
// 如果是在右节点删除
if ( root->rightNode == NULL ) {
// 如果右节点是空的话就代表插入路径有问题
return 0;
} else {
// 如果右节点是不是空的话就迭代下一步
return del_node_0(root->rightNode, &position[1], child);
}
} else if (position[0] == 'l') {
// 如果是在左节点删除
if ( root->leftNode == NULL ) {
return 0;
} else {
return del_node_0(root->leftNode, &position[1], child);
}
} else {
return 0;
}
}
}
/**
* @brief 删除度为2的节点
*
* @param root 根节点
* @param position 要删除节点的父节点相对于根节点的位置
* @param child 要删除节点是父节点的哪个孩子
* @param inheritChild 取代父节点的孩子
* @param insertPosition 另一个孩子的父节点在取代原父节点的哪个位置
* @param insertChild 另一个孩子是新父节点的哪个孩子
*
* @return 是否成功
**/
bool del_node_2(Fork root, char * position, char child, char inheritChild, char * insertPosition, char insertChild ) {
if ( position[0] == '\0' ) {
// 如果这个字符已经到结尾了 那么就在这个位置添加
if ( child == 'r' ) {
// 如果在右节点删除的话
if ( root->rightNode == NULL ) {
// 右节点是空的话就不删除
return 0;
} else {
// 取出 要删除节点的左右节点
Fork leftChildTree = takeout_tree( root, (char*)"r", 'l' );
Fork rightChildTree = takeout_tree( root, (char*)"r", 'r' );
if ( inheritChild == 'l' ) {
// 如果左树为取代树的话 先把右树植入左树
if ( add_node(leftChildTree, insertPosition, insertChild, rightChildTree) ) {
// 右树植入成功的话植入删除节点并植入左树
del_node_0(root, position, child );
add_node(root, position, child, leftChildTree);
return 1;
} else {
// 失败的话复原
add_node(root, (char*)"r", 'l', leftChildTree);
add_node(root, (char*)"r", 'r', rightChildTree);
return 0;
}
} else if ( inheritChild == 'r' ) {
// 如果先植入右树的话
// 如果先植入右树的话 先把左树植入左树
if ( add_node(rightChildTree, insertPosition, insertChild, leftChildTree) ) {
// 左树植入成功的话植入 删除节点并植入右树
del_node_0(root, position, child );
add_node(root, position, child, rightChildTree);
return 1;
} else {
// 失败的话复原
add_node(root, (char*)"r", 'l', leftChildTree);
add_node(root, (char*)"r", 'r', rightChildTree);
return 0;
}
} else {
return 0;
}
}
} else if (child == 'l') {
// 如果在左节点添加的话
if ( root->leftNode == NULL ) {
// 左节点是空的话就不删除
return 0;
} else {
// 取出 要删除节点的左右节点
Fork leftChildTree = takeout_tree( root, (char*)"l", 'l' );
Fork rightChildTree = takeout_tree( root, (char*)"l", 'r' );
if ( inheritChild == 'l' ) {
// 如果左树为取代树的话 先把右树植入左树
if ( add_node(leftChildTree, insertPosition, insertChild, rightChildTree) ) {
// 右树植入成功的话植入删除节点并植入左树
del_node_0(root, position, child );
add_node(root, position, child, leftChildTree);
return 1;
} else {
// 失败的话复原
add_node(root, (char*)"l", 'l', leftChildTree);
add_node(root, (char*)"l", 'r', rightChildTree);
return 0;
}
} else if ( inheritChild == 'r' ) {
// 如果先植入右树的话
// 如果先植入右树的话 先把左树植入左树
if ( add_node(rightChildTree, insertPosition, insertChild, leftChildTree) ) {
// 左树植入成功的话植入 删除节点并植入右树
del_node_0(root, position, child );
add_node(root, position, child, rightChildTree);
return 1;
} else {
// 失败的话复原
add_node(root, (char*)"l", 'l', leftChildTree);
add_node(root, (char*)"l", 'r', rightChildTree);
return 0;
}
} else {
return 0;
}
}
} else {
return 0;
}
} else {
// 这个字符不是结尾
if ( position[0] == 'r' ) {
// 如果是在右节点删除
if ( root->rightNode == NULL ) {
// 如果右节点是空的话就代表插入路径有问题
return 0;
} else {
// 如果右节点是不是空的话就迭代下一步
return del_node_2(root->rightNode, &position[1], child, inheritChild, insertPosition, insertChild );
}
} else if (position[0] == 'l') {
// 如果是在左节点删除
if ( root->leftNode == NULL ) {
return 0;
} else {
return del_node_2(root->leftNode, &position[1], child, inheritChild, insertPosition, insertChild );
}
} else {
return 0;
}
}
}
/**
* @brief 前序遍历
*
* @param root
**/
void pre_order(Fork root) {
if (root == NULL) {
printf("%s \n", "NULL");
return;
}
printf("data -> %d \n", root->data);
pre_order(root->leftNode);
pre_order(root->rightNode);
}
/**
* @brief 中序遍历
*
* @param root
**/
void ino_order(Fork root) {
if (root == NULL) {
printf("%s \n", "NULL");
return;
}
ino_order(root->leftNode);
printf("data -> %d \n", root->data);
ino_order(root->rightNode);
}
/**
* @brief 后序遍历
*
* @param root
**/
void pos_order(Fork root) {
if (root == NULL) {
printf("%s \n", "NULL");
return;
}
pos_order(root->leftNode);
pos_order(root->rightNode);
printf("data -> %d \n", root->data);
}
//层序遍历
void level_order(Fork root) {
queue <Fork> q;
if ( root == NULL ) {
return ;
} else {
q.push(root);
while (!q.empty()) {
Fork node = q.front();
printf("data -> %d \n", node->data);
q.pop();
if ( node->leftNode != NULL ) {
q.push( node->leftNode);
}
if ( node->rightNode != NULL) {
q.push( node->rightNode);
}
}
}
}
⭐友情链接⭐
二叉查找树Binart Search Tree (BST)基础 与 C语言实现 二叉查找树:https://blog.csdn.net/apple_53792700/article/details/127992980
👋都看到这里了,还不快点个关注?
✍️本文作者为 > 【谢玄.】 Mr-XieXuan < 于 2022/10/28/3:00 发布于 CSDN 。
⌨️GitHub: [ https://github.com/MR-XieXuan }
🔍个人私站: [ https://main.mrxie.xyz/ ]