前言
介绍
🍃数据结构专区:数据结构
参考
该部分知识参考于《数据结构(C语言版 第2版)》116 ~ 122页 及 《数据结构教程》201 ~ 213页
重点
树的基本实现并不难,重点在于对递归的理解才是树这部分知识带来的最大收获,因为树的逻辑结构就保证了使用递归思路来解决是最优的算法
由于树的基础知识太多,这里只进行简单解释,本篇主要是对基本操作代码的解释,后序的文章会跟进树的相关知识和性质的
🌈每一个清晨,都是世界对你说的最温柔的早安:ૢ(≧▽≦)و✨
目录
前言
1、二叉树的基础知识
1.1、定义与特点
1.2、基本形态与类型
1.3、性质与存储
1.4、遍历方法
2、链表树的操作
2.1 宏定义及结构体定义
2.2 前中后序遍历法
2.3 初始化树
2.4 创建新节点
2.5 创建树
2.6 销毁树
2.7 判空
2.8 求树的深度
2.9 根据值查找节点
2.10 求树根
2.11 求结点总数
2.12 输出叶子节点
2.13 计算值为x的节点所在的层数
2.14 计算第k层的节点数
2.15 复制二叉树
2.16 判断两棵树是否相似
2.17 查找某节点的双亲节点
2.18 其他一些不重要函数
2.19 整体代码(含测试代码)
结语
1、二叉树的基础知识
1.1、定义与特点
定义:二叉树(Binary Tree)是树形结构的一个重要类型,是一种每个节点最多有两个子树(左子树和右子树)的有序树。
特点:
- 每个节点最多只能有两棵子树,且有左右之分,次序不能颠倒。
- 二叉树可以是空树,或者由根节点和左、右子树组成,其中左子树和右子树也同样是二叉树。
1.2、基本形态与类型
基本形态:
- 空二叉树:没有任何节点的二叉树。
- 只有一个根节点的二叉树。
- 只有左子树或只有右子树的二叉树。
- 完全二叉树:深度为k,有n个节点的二叉树,当且仅当其每一个节点都与深度为k的满二叉树中编号从1到n的节点一一对应时,称为完全二叉树。
- 满二叉树:如果一棵二叉树只有度为0的节点和度为2的节点,并且度为0的节点在同一层上,则这棵二叉树为满二叉树。
特殊类型:
- 二叉查找树(Binary Search Tree):左子树上的所有节点值均小于根节点值,右子树上的所有节点值均大于根节点值。
- 平衡二叉树(如AVL树):左右子树都是平衡二叉树,且所有节点左、右子树深度之差的绝对值不大于1。
- 红黑树:除了具备二叉查找树的特性外,还有额外的特性以保持树的自平衡。
- 哈夫曼树(Huffman Tree):带权路径长度最短的树,常用于通信中的压缩编码。
1.3、性质与存储
性质:
- 二叉树的第i层上至多有2^(i-1)个节点。
- 深度为h的二叉树中至多含有2^h-1个节点。
- 在任意一棵二叉树中,若叶子节点数为n0,度为2的节点数为n2,则必有n0=n2+1。
存储方式:
- 顺序存储:使用数组来存储二叉树的节点,通常适用于完全二叉树。
- 链式存储:使用链表来存储二叉树的节点,每个节点包含数据域和指向左、右子节点的指针。
1.4、遍历方法
遍历:是对树的一种最基本的运算,指按一定的规则和顺序走遍二叉树的所有节点,使每一个节点都被访问一次,而且只被访问一次。
遍历方法:
- 先序遍历(Preorder Traversal):访问根节点,然后遍历左子树,最后遍历右子树。
- 中序遍历(Inorder Traversal):遍历左子树,访问根节点,然后遍历右子树。中序遍历二叉排序树可得到一个关键字的有序序列。
- 后序遍历(Postorder Traversal):遍历左子树,遍历右子树,最后访问根节点。
2、链表树的操作
一、由于二叉树的顺序存储结构具有顺序存储结构的固有缺陷,使得二叉树的插入、删除等运算十分麻烦,所以对于一般二叉树通常采用链式存储方式
二、这里我采用左右孩子的存储方法
三、下面我会先把各函数代码进行简单介绍并粘贴出来,若有不懂的可以向下翻找会有函数的详细介绍
2.1 宏定义及结构体定义
#include<stdio.h>
#include<iostream>
using namespace std;
typedef char TElemType;
typedef struct BiTNode {
TElemType data;
struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
2.2 前中后序遍历法
//前序递归遍历法
void PreOrder(BiTree T)
{
if (T != NULL)
{
cout << T->data << " ";
PreOrder(T->lchild);
PreOrder(T->rchild);
}
}
//中序递归遍历法
void InOrder(BiTree T)
{
if (T != NULL)
{
InOrder(T->lchild);
cout << T->data << " ";
InOrder(T->rchild);
}
}
//后序递归遍历法
void PostOrder(BiTree T)
{
if (T != NULL)
{
PostOrder(T->lchild);
PostOrder(T->rchild);
cout << T->data << " ";
}
}
2.3 初始化树
//初始化树
void InitTree(BiTree& T)
{
T = NULL;
}
2.4 创建新节点
//创建新节点
BiTNode* CreatNewNode(TElemType value)
{
BiTNode* node = new BiTNode;
//等价于
//BiTNode* node = (BiTNode*)malloc(sizeof(BiTNode));
node->data = value;
node->lchild = NULL;
node->rchild = NULL;
return node;
}
2.5 创建树
//创建树
void CreateTree(BiTree& T, char* definition)
{
static int i = 0; //静态变量,每次递归进入过程中都会i++
char ch = definition[i++];
if (ch == '#')
{
T = NULL;
}
else
{
T = CreatNewNode(ch);
CreateTree(T->lchild, definition);
CreateTree(T->rchild, definition);
}
}
2.6 销毁树
//销毁树
void DestroyTree(BiTree& T)
{
if (T != NULL)
{
DestroyTree(T->lchild);
DestroyTree(T->rchild);
free(T);
T = NULL;
}
}
2.7 判空
//判空
int TreeEmpty(BiTree T)
{
if (T == NULL)
{
return 1;
}
else
{
return 0;
}
}
2.8 求树的深度
//求树的深度
int TreeDepth(BiTree T)
{
//定义左右子树的高度,作为最后返回时的比较值
int lchildh = 0, rchildh = 0;
if (T == NULL)
{
return 0;
}
else
{
lchildh = TreeDepth(T->lchild);
rchildh = TreeDepth(T->rchild);
return lchildh > rchildh ? lchildh + 1 : rchildh + 1;
}
}
2.9 根据值查找节点
//查找某个节点
BiTNode* FindNode(BiTree T, TElemType x)
{
BiTNode* p;//作为遍历
if (T == NULL)
{
return NULL;
}
if (T->data == x)
{
return T;
}
//左递归查找
p = FindNode(T->lchild, x);
if (p != NULL)
{
return p;
}
else
{
//否则进行右递归查找
return FindNode(T->rchild, x);
}
}
2.10 求树根
//求树根
BiTNode* Root(BiTree T)
{
if (T == NULL)
{
printf("ERROR:Tree is NULL!");
}
return T;
}
2.11 求结点总数
//求结点总数
int Nodes(BiTree T)
{
if (T == NULL)
{
return 0;
}
return Nodes(T->lchild) + Nodes(T->rchild) + 1;
}
2.12 输出叶子节点
//输出叶子结点
void DispLeaf(BiTree T)
{
if (T != NULL)
{
if (T->lchild == NULL && T->rchild == NULL)
{
cout << T->data << " "; //访问叶子节点
}
DispLeaf(T->lchild); //输出左子树中的叶子节点
DispLeaf(T->rchild); //输出右子树中的叶子节点
}
}
2.13 计算值为x的节点所在的层数
//计算某个节点值为x的层次(若在根结点 h = 1,树为空h = 0)
int Level(BiTree T, TElemType x, int h)
{
int l;
if (T == NULL)
{
return(0);
}
if (T->data == x)
{
return(h);
}
l = Level(T->lchild, x, h + 1);
if (l != 0)
{
return(l);
}
else
{
return(Level(T->rchild, x, h + 1));
}
}
2.14 计算第k层的节点数
//计算第k层的结点数
void LnodeNum(BiTree T, int h, int k, int& n)
{
if (T == NULL)
{
return;//空树直接返回
}
//处理非空树
if (h == k)
n++; //当前访问的结点在第k层时,n++
else if (h < k) //若当前节点层次小于k,递归处理左右子树
{
LnodeNum(T->lchild, h + 1, k, n);
LnodeNum(T->rchild, h + 1, k, n);
}
}
2.15 复制二叉树
//复制二叉树
void Copy(BiTree T, BiTree& NewT)
{
//复制一棵和T完全相同的二叉树
if (T == NULL)
{
NewT = NULL;
return;
}
else
{
NewT = new BiTNode;
NewT->data = T->data;
Copy(T->lchild, NewT->lchild);
Copy(T->rchild, NewT->rchild);
}
}
2.16 判断两棵树是否相似
//判断两棵树是否相似
bool Like(BiTree T1, BiTree T2)
{
bool like1, like2;
if (T1 == NULL && T2 == NULL)
{
return true;
}
else if (T1 == NULL || T2 == NULL)
{
return false;
}
else
{
like1 = Like(T1->lchild, T2->lchild);
like2 = Like(T1->rchild, T2->rchild);
return(like1 && like2);
}
}
2.17 查找某节点的双亲节点
//查找某节点的双亲节点
//这里需要用到两个函数来完成
//1.辅助函数
BiTNode* ParentHelper(BiTNode* root, BiTNode* cur_e)
{
if (root == NULL || cur_e == NULL)
{
return NULL;
}
//如果root节点的左右节点中某一个是目标节点
if (root->lchild == cur_e || root->rchild == cur_e)
{
return root;
}
//进行左递归查找
BiTNode* leftResult = ParentHelper(root->lchild, cur_e);
if (leftResult != NULL)
{
return leftResult;
}
//否则递归查找右子树
return ParentHelper(root->rchild, cur_e);
}
//2.查询函数
BiTNode* Parent(BiTree& T, BiTNode* cur_e)
{
if (T == NULL || cur_e == NULL)
{
return NULL;
}
//调用辅助函数查找双亲节点
return ParentHelper(T, cur_e);
}
2.18 其他一些不重要函数
//不重要函数
//求某个节点的值
TElemType Value(BiTree& T, BiTNode* cur_e)
{
if (T == NULL || cur_e == NULL)
{
printf("ERROR:Tree or Node is NULL!");
exit(1);
}
return cur_e->data;
}
//给某个节点赋值
void Assign(BiTree& T, BiTNode* cur_e, TElemType value)
{
if (T == NULL || cur_e == NULL)
{
printf("ERROR:Tree or Node is NULL!");
exit(1);
}
cur_e->data = value;
}
//求某节点的左孩子结点
BiTNode* LeftChild(BiTree T, BiTNode* cur_e)
{
if (T == NULL || cur_e == NULL)
{
printf("ERROR: Tree or cur_e is NULL!");
exit(1);
}
return cur_e->lchild;
}
//求某节点的右孩子结点
BiTNode* RightChild(BiTree T, BiTNode* cur_e)
{
if (T == NULL || cur_e == NULL)
{
printf("ERROR: Tree or cur_e if NULL!");
exit(1);
}
return cur_e->rchild;
}
2.19 整体代码(含测试代码)
#include<stdio.h>
#include<iostream>
using namespace std;
typedef char TElemType;
typedef struct BiTNode {
TElemType data;
struct BiTNode* lchild, * rchild;
}BiTNode, * BiTree;
//前序递归遍历法
void PreOrder(BiTree T)
{
if (T != NULL)
{
cout << T->data << " ";
PreOrder(T->lchild);
PreOrder(T->rchild);
}
}
//中序递归遍历法
void InOrder(BiTree T)
{
if (T != NULL)
{
InOrder(T->lchild);
cout << T->data << " ";
InOrder(T->rchild);
}
}
//后序递归遍历法
void PostOrder(BiTree T)
{
if (T != NULL)
{
PostOrder(T->lchild);
PostOrder(T->rchild);
cout << T->data << " ";
}
}
//初始化树
void InitTree(BiTree& T)
{
T = NULL;
}
//创建新节点
BiTNode* CreatNewNode(TElemType value)
{
BiTNode* node = new BiTNode;
//等价于
//BiTNode* node = (BiTNode*)malloc(sizeof(BiTNode));
node->data = value;
node->lchild = NULL;
node->rchild = NULL;
return node;
}
//创建树
void CreateTree(BiTree& T, char* definition)
{
static int i = 0; //静态变量,每次递归进入过程中都会i++
char ch = definition[i++];
if (ch == '#')
{
T = NULL;
}
else
{
T = CreatNewNode(ch);
CreateTree(T->lchild, definition);
CreateTree(T->rchild, definition);
}
}
//销毁树
void DestroyTree(BiTree& T)
{
if (T != NULL)
{
DestroyTree(T->lchild);
DestroyTree(T->rchild);
free(T);
T = NULL;
}
}
//清空树
//在C代码作为主要函数的代码下,清空树和销毁树是几乎一样的操作
//唯一差别在于,销毁树是树根也销毁,清空树保存其树根
//如果是C++代码下
//BinaryTree类中添加了一个析构函数(~BinaryTree()),它会在对象被删除时自动调用DestroyTree。
//这确保了即使用户忘记显式调用DestroyTree,内存也会被正确释放
//判空
int TreeEmpty(BiTree T)
{
if (T == NULL)
{
return 1;
}
else
{
return 0;
}
}
//求树的深度
int TreeDepth(BiTree T)
{
//定义左右子树的高度,作为最后返回时的比较值
int lchildh = 0, rchildh = 0;
if (T == NULL)
{
return 0;
}
else
{
lchildh = TreeDepth(T->lchild);
rchildh = TreeDepth(T->rchild);
return lchildh > rchildh ? lchildh + 1 : rchildh + 1;
}
}
//查找某个节点
BiTNode* FindNode(BiTree T, TElemType x)
{
BiTNode* p;//作为遍历
if (T == NULL)
{
return NULL;
}
if (T->data == x)
{
return T;
}
//左递归查找
p = FindNode(T->lchild, x);
if (p != NULL)
{
return p;
}
else
{
//否则进行右递归查找
return FindNode(T->rchild, x);
}
}
//求树根
BiTNode* Root(BiTree T)
{
if (T == NULL)
{
printf("ERROR:Tree is NULL!");
}
return T;
}
//求结点总数
int Nodes(BiTree T)
{
if (T == NULL)
{
return 0;
}
return Nodes(T->lchild) + Nodes(T->rchild) + 1;
}
//输出叶子结点
void DispLeaf(BiTree T)
{
if (T != NULL)
{
if (T->lchild == NULL && T->rchild == NULL)
{
cout << T->data << " "; //访问叶子节点
}
DispLeaf(T->lchild); //输出左子树中的叶子节点
DispLeaf(T->rchild); //输出右子树中的叶子节点
}
}
//计算某个节点值为x的层次(若在根结点 h = 1,树为空h = 0)
int Level(BiTree T, TElemType x, int h)
{
int l;
if (T == NULL)
{
return(0);
}
if (T->data == x)
{
return(h);
}
l = Level(T->lchild, x, h + 1);
if (l != 0)
{
return(l);
}
else
{
return(Level(T->rchild, x, h + 1));
}
}
//计算第k层的结点数
void LnodeNum(BiTree T, int h, int k, int& n)
{
if (T == NULL)
{
return;//空树直接返回
}
//处理非空树
if (h == k)
n++; //当前访问的结点在第k层时,n++
else if (h < k) //若当前节点层次小于k,递归处理左右子树
{
LnodeNum(T->lchild, h + 1, k, n);
LnodeNum(T->rchild, h + 1, k, n);
}
}
//复制二叉树
void Copy(BiTree T, BiTree& NewT)
{
//复制一棵和T完全相同的二叉树
if (T == NULL)
{
NewT = NULL;
return;
}
else
{
NewT = new BiTNode;
NewT->data = T->data;
Copy(T->lchild, NewT->lchild);
Copy(T->rchild, NewT->rchild);
}
}
//判断两棵树是否相似
bool Like(BiTree T1, BiTree T2)
{
bool like1, like2;
if (T1 == NULL && T2 == NULL)
{
return true;
}
else if (T1 == NULL || T2 == NULL)
{
return false;
}
else
{
like1 = Like(T1->lchild, T2->lchild);
like2 = Like(T1->rchild, T2->rchild);
return(like1 && like2);
}
}
//查找某节点的双亲节点
//这里需要用到两个函数来完成
//1.辅助函数
BiTNode* ParentHelper(BiTNode* root, BiTNode* cur_e)
{
if (root == NULL || cur_e == NULL)
{
return NULL;
}
//如果root节点的左右节点中某一个是目标节点
if (root->lchild == cur_e || root->rchild == cur_e)
{
return root;
}
//进行左递归查找
BiTNode* leftResult = ParentHelper(root->lchild, cur_e);
if (leftResult != NULL)
{
return leftResult;
}
//否则递归查找右子树
return ParentHelper(root->rchild, cur_e);
}
//2.查询函数
BiTNode* Parent(BiTree& T, BiTNode* cur_e)
{
if (T == NULL || cur_e == NULL)
{
return NULL;
}
//调用辅助函数查找双亲节点
return ParentHelper(T, cur_e);
}
//不重要函数
//求某个节点的值
TElemType Value(BiTree& T, BiTNode* cur_e)
{
if (T == NULL || cur_e == NULL)
{
printf("ERROR:Tree or Node is NULL!");
exit(1);
}
return cur_e->data;
}
//给某个节点赋值
void Assign(BiTree& T, BiTNode* cur_e, TElemType value)
{
if (T == NULL || cur_e == NULL)
{
printf("ERROR:Tree or Node is NULL!");
exit(1);
}
cur_e->data = value;
}
//求某节点的左孩子结点
BiTNode* LeftChild(BiTree T, BiTNode* cur_e)
{
if (T == NULL || cur_e == NULL)
{
printf("ERROR: Tree or cur_e is NULL!");
exit(1);
}
return cur_e->lchild;
}
//求某节点的右孩子结点
BiTNode* RightChild(BiTree T, BiTNode* cur_e)
{
if (T == NULL || cur_e == NULL)
{
printf("ERROR: Tree or cur_e if NULL!");
exit(1);
}
return cur_e->rchild;
}
int main() {
BiTree T, NewT;
char definition[] = "ABD##E##CF##G#H##";
// 测试初始化和创建树
InitTree(T);
CreateTree(T, definition);
cout << "初始化树和创建树的测试:" << endl;
// 测试遍历
cout << "\n前序遍历: ";
PreOrder(T);
cout << "\n中序遍历: ";
InOrder(T);
cout << "\n后序遍历: ";
PostOrder(T);
// 测试TreeEmpty和TreeDepth
cout << "\n\n树是否为空? " << (TreeEmpty(T) ? "Yes" : "No");
cout << "\n树深: " << TreeDepth(T);
// 测试FindNode
TElemType searchValue = 'D';
BiTNode* foundNode = FindNode(T, searchValue);
cout << "\n\n查找节点 '" << searchValue << "': "
<< (foundNode ? "Found" : "Not found");
// 测试Root
BiTNode* root = Root(T);
cout << "\n树根: " << (root ? root->data : ' ');
// 测试Nodes和DispLeaf
cout << "\n所有节点数: " << Nodes(T);
cout << "\n叶子结点: ";
DispLeaf(T);
// 测试Level
cout << "\n\n'E'所在的层数: " << Level(T, 'E', 1);
// 测试LnodeNum
int nodeCount = 0;
LnodeNum(T, 1, 3, nodeCount);
cout << "\n第3层的结点数: " << nodeCount;
// 测试Copy
Copy(T, NewT);
cout << "\n\n复制一棵树,并进行中序遍历: ";
InOrder(NewT);
// 测试Like
cout << "\n上面复制的树和原树是否相似? " << (Like(T, NewT) ? "Yes" : "No");
// 测试Parent
BiTNode* nodeE = FindNode(T, 'E');
BiTNode* parentOfE = Parent(T, nodeE);
cout << "\n\n'E'的双亲节点: " << (parentOfE ? parentOfE->data : ' ');
// 清理内存
DestroyTree(T);
DestroyTree(NewT);
cout << "\n\nAll tests completed." << endl;
return 0;
}
结语
数据结构树这里的相关操作和知识点非常多,并且一环套一环,这里需要大家拥有较多的耐心来一步步走下去,相信一定会更好的!