阅读建议:
一、实验目的
二、实验内容
三、实验过程
四、代码结构
五、测试结果
阅读建议:
1.实验的软硬件环境要求:
(1)硬件环境要求:PC机
(2)软件环境要求:Windows 环境下的 Microsoft Visual Studio2.该实验采用了头文件(.h)和源文件(.cpp)相结合的形式。
1.熟练掌握二叉链的存储特点;
2.熟练掌握二叉树的基本操作;
3.熟练掌握基于二叉链的二叉树操作算法实现;
4.能灵活使用二叉树解决具体的问题。
1.定义二叉链类,实现二叉树的基本操作算法;
2.在主函数中定义对象,并调用成员函数,验证二叉链的基本操作。具体包括:
(1)建立二叉链存储的二叉树
(2)遍历二叉树:前序、中序、后序和层序
(3)求二叉树的深度
(4)交换二叉树所有结点的左右子树
(5)统计二叉树叶子结点的个数
(6)前序次序打印二叉树的叶子结点
(7)计算二叉树的最大宽度,即结点数目最多的那一层的结点个数
(8)非递归的方式先序或者后序遍历二叉树
注:操作算法可不局限于以上内容
1.结点结构定义
template
struct BiNode
{
T data;
BiNode* lchild, * rchild;
};
2.构造函数,根据输入的字符信息构建一棵二叉树。
工作原理:
- 函数首先定义了一个字符变量
ch
,用于存储从标准输入流中读取的字符。- 然后,函数使用
cin
从标准输入读取一个字符。- 如果读取的字符是
'#'
,则将传入的二叉树根节点指针bt
设置为NULL
,表示建立一棵空树。- 如果读取的字符不是
'#'
,则执行以下操作:
a. 为新节点动态分配内存,并将指针赋值给bt
。
b. 将读取的字符赋值给新节点的数据域。
c. 递归调用Creat
函数,分别构建左子树和右子树。这是通过将当前节点的左子节点和右子节点作为参数传递给Creat
函数来实现的。
d. 将构建的左子树和右子树分别赋值给当前节点的左子节点和右子节点。- 最后,返回当前节点的指针。
template
BiNode* BiTree::Creat(BiNode* bt)
{
char ch;
cin >> ch; //输入节点的数据信息,假设为字符
if (ch == '#') {
bt=NULL; //建立一棵空树
}else{
bt = new BiNode;
bt->data = ch;
bt->lchild = Creat(bt->lchild); //递归建立左子树
bt->rchild = Creat(bt->rchild); //递归建立右子树
}
return bt;
}
3.析构函数,用于释放二叉树所占用的内存资源。
工作原理:
- 函数首先检查传入的二叉树根节点指针
bt
是否为NULL
。如果是,则直接返回,不执行任何操作。- 如果
bt
不为NULL
,则执行以下操作:
a. 递归调用Release
函数,释放当前节点的左子树所占用的内存资源。这是通过将当前节点的左子节点作为参数传递给Release
函数来实现的。
b. 递归调用Release
函数,释放当前节点的右子树所占用的内存资源。这是通过将当前节点的右子节点作为参数传递给Release
函数来实现的。
c. 删除当前节点所占用的动态内存,即执行delete bt;
。- 最后,返回。
template
void BiTree::Release(BiNode* bt)
{
if (bt == NULL) {
return;
}else{
Release(bt->lchild);
Release(bt->rchild);
delete bt;
}
}
4.前序遍历。
工作原理:
- 函数首先检查传入的二叉树根节点指针
bt
是否为NULL
。如果是,则直接返回,不执行任何操作。- 如果
bt
不为NULL
,则执行以下操作:
a. 输出当前节点的数据域的值。
b. 递归调用PreOrder
函数,对当前节点的左子树进行前序遍历。这是通过将当前节点的左子节点作为参数传递给PreOrder
函数来实现的。
c. 递归调用PreOrder
函数,对当前节点的右子树进行前序遍历。这是通过将当前节点的右子节点作为参数传递给PreOrder
函数来实现的。- 最后,返回。
template
void BiTree::PreOrder(BiNode* bt)
{
if (bt == NULL) {
return;
}
else
{
cout << bt->data << " ";
PreOrder(bt->lchild);
PreOrder(bt->rchild);
}
}
5.中序遍历。
工作原理:
- 函数首先检查传入的二叉树根节点指针
bt
是否为NULL
。如果是,则直接返回,不执行任何操作。- 如果
bt
不为NULL
,则执行以下操作:
a. 递归调用InOrder
函数,对当前节点的左子树进行中序遍历。这是通过将当前节点的左子节点作为参数传递给InOrder
函数来实现的。
b. 输出当前节点的数据域的值。
c. 递归调用InOrder
函数,对当前节点的右子树进行中序遍历。这是通过将当前节点的右子节点作为参数传递给InOrder
函数来实现的。- 最后,返回。
template
void BiTree::InOrder(BiNode* bt)
{
if (bt == NULL) {
return;
}
InOrder(bt->lchild);
cout << bt->data << " ";
InOrder(bt->rchild);
}
6.后序遍历。
工作原理:
- 函数首先检查传入的二叉树根节点指针
bt
是否为NULL
。如果是,则直接返回,不执行任何操作。- 如果
bt
不为NULL
,则执行以下操作:
a. 递归调用PostOrder
函数,对当前节点的左子树进行后序遍历。这是通过将当前节点的左子节点作为参数传递给PostOrder
函数来实现的。
b. 递归调用PostOrder
函数,对当前节点的右子树进行后序遍历。这是通过将当前节点的右子节点作为参数传递给PostOrder
函数来实现的。
c. 输出当前节点的数据域的值。- 最后,返回。
template
void BiTree::PostOrder(BiNode* bt)
{
if (bt == NULL) {
return;
}
PostOrder(bt->lchild);
PostOrder(bt->rchild);
cout << bt->data << " ";
}
7.层序遍历。
工作原理:
- 定义一个队列
Q
,用于存储二叉树的节点。队列具有两个指针front
和rear
,分别表示队首和队尾的位置。- 初始化队列为空,并将队首和队尾指针设置为-1。
- 检查二叉树的根节点是否为空。如果为空,则直接返回。
- 将根节点加入队列中,更新队尾指针。
- 进入循环,直到队首指针与队尾指针相等为止。
- 在循环中,首先从队列中取出一个节点,并输出该节点的数据域的值。
- 然后,递归地将该节点的左子节点和右子节点加入队列中,更新队尾指针。
- 继续循环,直到队列为空。
- 返回。
template
void BiTree::LevelOrder()
{
BiNode* q = NULL, * Q[100];
int front = -1, rear = -1; //队列初始化
if (root == NULL) return;
Q[++rear] = root;
while (front != rear)
{
q = Q[++front];
cout << q->data << " ";
if (q->lchild != NULL) Q[++rear] = q->lchild;
if (q->rchild != NULL) Q[++rear] = q->rchild;
}
}
8.求二叉树的深度。
工作原理:
- 如果传入的二叉树根节点指针
bt
为空,则返回深度为0。- 如果
bt
不为空,则递归地计算左子树和右子树的深度。- 比较左子树和右子树的深度,返回较大的深度值,并加上1(当前节点的深度)。
- 返回最终计算得到的深度值。
template
int BiTree::GetDepth(BiNode* bt)
{
if (bt == NULL) {
return 0;
}else{
int leftDepth = GetDepth(bt->lchild);
int rightDepth = GetDepth(bt->rchild);
return (leftDepth > rightDepth ? leftDepth : rightDepth) + 1;
}
}
9.交换二叉树所有结点的左右子树。
工作原理:
- 如果传入的二叉树根节点指针
bt
为空,则直接返回,不执行任何操作。- 如果
bt
不为空,则交换当前节点的左右子树。- 递归地调用
SwapSubTrees
函数,交换当前节点的左子树和右子树的所有子节点。- 递归地调用
SwapSubTrees
函数,继续交换左子树和右子树的所有子节点。- 返回。
template
void BiTree::SwapSubTrees(BiNode* bt) {
if (bt == NULL) {
return;
}
swap(bt->lchild, bt->rchild);
SwapSubTrees(bt->lchild);
SwapSubTrees(bt->rchild);
}
10.统计二叉树叶子结点的个数。
工作原理:
- 如果传入的二叉树根节点指针
bt
为空,则返回叶子节点的个数为0。- 如果当前节点为叶子节点(即左右子树都为空),则返回叶子节点的个数为1。
- 如果当前节点不是叶子节点,则递归地统计左子树和右子树的叶子节点个数,并将它们相加。
- 返回最终计算得到的叶子节点个数。
template
int BiTree::CountLeaves(BiNode* bt) {
if (bt == NULL) {
return 0;
}else if (bt->lchild == NULL && bt->rchild == NULL) {
return 1;
}else {
return CountLeaves(bt->lchild) + CountLeaves(bt->rchild);
}
}
11.前序次序打印二叉树的叶子结点。
工作原理:
- 如果传入的二叉树根节点指针
bt
为空,则直接返回,不执行任何操作。- 如果当前节点是叶子节点(即左右子树都为空),则输出该节点的数据域的值。
- 递归地调用
PrintLeafNodesInPreOrder
函数,先遍历左子树,再遍历右子树。- 返回。
template
void BiTree::PrintLeafNodesInPreOrder(BiNode* bt) {
if (bt == NULL) {
return;
}
else if (bt->lchild == NULL && bt->rchild == NULL) {
cout << bt->data << " ";
}
PrintLeafNodesInPreOrder(bt->lchild);
PrintLeafNodesInPreOrder(bt->rchild);
}
12.计算二叉树的最大宽度。
工作原理:
- 如果二叉树的根节点指针
bt
为空,即二叉树为空,则返回0,表示最大宽度为0。- 创建一个队列
q
,用于进行广度优先搜索(BFS)遍历。- 将根节点加入队列中。
- 初始化最大宽度
maxWidth
为0。- 进入循环,直到队列为空。
- 在循环中,首先获取当前层的节点数量
levelSize
。- 更新最大宽度
maxWidth
为当前层节点数量和当前最大宽度的较大值。- 遍历当前层的所有节点,并将它们的子节点加入队列中。
- 继续循环,直到队列为空。
- 返回最终计算得到的最大宽度
maxWidth
。
template
int BiTree::GetMaxWidth(BiNode* bt) {
if (root == NULL) return 0; // 空树的情况
queue*> q; // 使用队列来进行BFS遍历
q.push(root);
int maxWidth = 0; // 记录最大宽度
while (!q.empty()) {
int levelSize = q.size(); // 当前层的节点数量
maxWidth = max(maxWidth, levelSize); // 更新最大宽度
// 遍历当前层的所有节点,并将它们的子节点加入队列
for (int i = 0; i < levelSize; i++) {
BiNode* currentNode = q.front();
q.pop();
if (currentNode->lchild != NULL) {
q.push(currentNode->lchild);
}
if (currentNode->rchild != NULL) {
q.push(currentNode->rchild);
}
}
}
return maxWidth;
}
13.非递归的方式先序遍历二叉树。
工作原理:
- 如果二叉树的根节点指针
root
为空,即二叉树为空,则直接返回,不执行任何操作。- 创建一个栈
stk
,用于辅助遍历。- 将根节点入栈。
- 进入循环,直到栈为空。
- 在循环中,首先取出栈顶元素,即当前节点。
- 访问当前节点,输出其数据域的值。
- 将当前节点的右子节点入栈。
- 将当前节点的左子节点入栈。
- 继续循环,直到栈为空。
template
void BiTree::PreOrderTraversal() {
if (root == NULL) return; // 空树的情况,直接返回
stack*> stk; // 使用栈来辅助遍历
stk.push(root); // 将根节点入栈
while (!stk.empty()) {
BiNode* currentNode = stk.top(); // 取出栈顶元素
stk.pop();
cout << currentNode->data << " "; // 访问当前节点
// 先将右孩子入栈,再将左孩子入栈(因为栈是后进先出的)
if (currentNode->rchild != NULL) {
stk.push(currentNode->rchild);
}
if (currentNode->lchild != NULL) {
stk.push(currentNode->lchild);
}
}
}
14.主函数
int main()
{
cout << "请输入二叉树的前序遍历序列(#表示空节点):" << endl;
BiTreeT; //定义对象变量T
cout << "前序遍历:";
T.PreOrder();
cout << "\n中序遍历:";
T.InOrder();
cout << "\n后序遍历:";
T.PostOrder();
cout << "\n层序遍历:";
T.LevelOrder();
cout << "\n二叉树的深度:";
cout<
完整代码链接:https://download.csdn.net/download/weixin_73286497/88758670
希望大家可以在该篇实验报告中有所收获,同时也感谢各位大佬的支持。文章如有任何问题请在评论区留言斧正,鸿蒙会尽快回复您的建议!