前言
介绍
🍃数据结构专区:数据结构
参考
该部分知识参考于《数据结构(C语言版 第2版)》116 ~ 122页 及 《数据结构教程》201 ~ 213页
重点
树的基本实现并不难,重点在于对递归的理解才是树这部分知识带来的最大收获,因为树的逻辑结构就保证了使用递归思路来解决是最优的算法
由于树的基础知识太多,这里只进行简单解释,本篇主要是对基本操作代码的解释,后序的文章会跟进树的相关知识和性质的
🌈每一个清晨,都是世界对你说的最温柔的早安:ૢ(≧▽≦)و✨
目录
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;
}
结语
数据结构树这里的相关操作和知识点非常多,并且一环套一环,这里需要大家拥有较多的耐心来一步步走下去,相信一定会更好的!