上机实验三 图的最小生成树算法设计 西安石油大学数据结构

news2024/11/19 16:46:38

二叉树设计

实验名称:二叉树设计

(1)实验目的:

1) 掌握二叉树的逻辑结构。

2) 掌握二叉树的二叉链表存储结构;

3) 掌握基于二叉链表存储的二叉树的遍历等操作的实现。

(2)主要内容:

1) 定义二叉链存储结构。

2) 实现二叉树的建立(利用扩展先序序列建立二叉链表存储的二叉树)、二叉树的遍历、统计二叉树结点数、求二叉树高度、打印二叉树等操作。

3) 编写一个测试主函数,建立如下二叉树,并测试所设计的算法。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

二叉树的基本概念

二叉树是一种常见的树形数据结构,每个节点最多有两个子节点,分别称为左节点和右节点。如果一个节点没有左或右子节点,则对应的子节点为空。二叉树可以为空,如果不为空,则必须包含一个根节点。

二叉树的定义可以使用递归方式来描述,即一个二叉树要么为空,要么由一个根节点和两个分别为左子树和右子树的二叉树组成。在实际应用中,二叉树通常用于表示层次结构、搜索树等。

二叉树的遍历方式包括前序遍历(根-左-右)、中序遍历(左-根-右)和后序遍历(左-右-根)。其中,前序遍历顺序是:先访问根节点,然后依次遍历左子树和右子树;中序遍历顺序是:先访问左子树,然后遍历根节点,最后遍历右子树;后序遍历顺序是:先访问左子树,然后依次遍历右子树和根节点。

二叉树还有一种广义的遍历方式,叫做层序遍历,也称为广度优先遍历。层序遍历按照从上到下、从左到右的顺序依次访问每个节点。

二叉树的应用非常广泛,比如在搜索算法中用于二分查找和决策树等。

二叉树是一种树形数据结构,在其中每个节点最多有两个子节点。以下是二叉树的一些基本概念:

  1. 根节点(Root Node):二叉树的顶层节点,没有父节点。它是整个二叉树的起点。

  2. 子节点(Child Node):一个节点的直接下方节点称为其子节点。一个节点最多可以有两个子节点,分别称为左子节点和右子节点。

  3. 父节点(Parent Node):一个节点的直接上方节点称为其父节点。

  4. 叶节点(Leaf Node):没有子节点的节点称为叶节点,也可以称为终端节点。

  5. 内部节点(Internal Node):除了叶节点以外的所有节点都被称为内部节点。

  6. 兄弟节点(Sibling Node):具有相同父节点的节点被称为兄弟节点。

  7. 节点的度(Node Degree):节点的度表示它拥有的子节点数目,在二叉树中,节点的度最大为2。

  8. 节点的层级(Node Level):根节点的层级为0,其他节点的层级等于其父节点的层级加1。

  9. 树的高度(Tree Height):树的高度是指从根节点到最远叶节点的层数。

  10. 完全二叉树(Complete Binary Tree):除了最后一层外,每一层的节点都被填满,且最后一层的节点都靠左排列。

  11. 满二叉树(Full Binary Tree):除了叶节点外,每个节点都有两个子节点。

二叉树常见用途

二叉树在计算机科学和软件工程中有广泛的应用。以下是二叉树的一些常见用途:

  1. 搜索算法:二叉搜索树(BST)是一种特殊的二叉树,其中左子树的节点值小于根节点,右子树的节点值大于根节点。这种特性使得二叉搜索树非常适合实现搜索算法,例如二分查找。

  2. 排序算法:堆排序(Heap Sort)是一种基于二叉堆(Binary Heap)数据结构的排序算法。二叉堆是一种特殊的完全二叉树,具有堆属性,可以高效地进行插入、删除最大/最小元素等操作。

  3. 表达式求值:二叉表达式树(Expression Tree)可以用于解析和求值数学表达式。每个操作符作为一个节点,其左右子节点分别表示操作符的操作数。

  4. 文件系统和目录结构:二叉树可以用于建模文件系统和目录结构。每个节点表示一个目录或文件,左子节点和右子节点连接到下级目录或文件。

  5. 线索二叉树:线索二叉树(Threaded Binary Tree)可以优化二叉树的遍历过程。通过添加前驱和后继指针,可以避免使用递归或栈来实现遍历。

  6. 数据压缩:霍夫曼树(Huffman Tree)是一种特殊的二叉树,用于数据压缩中的霍夫曼编码。在霍夫曼编码中,频率较高的字符被分配较短的编码,从而实现数据的高效压缩。

  7. 机器学习和决策树:决策树是一种基于二叉树结构的分类和回归模型。每个节点表示一个属性或特征,根据不同属性的取值进行分支,最终到达叶节点表示分类或回归结果。

除了上述用途,二叉树还可以作为其他数据结构的基础,例如AVL树、红黑树等。对于广义的树形结构,二叉树可以通过适当的扩展和变形来表示和处理,提供了更大的灵活性和效率。

二叉树的二叉链表存储结构

二叉树的二叉链表存储结构是指使用指向左右子节点的指针,将每个节点的数据和其左右子节点连接起来,以构成二叉树。具体来说,每个节点包含三个域:数据域、左子节点指针域和右子节点指针域,如下所示:

struct BinaryTreeNode {
    int data;
    BinaryTreeNode* left_child;
    BinaryTreeNode* right_child;
};

在二叉链表存储结构中,每个节点都包含一个数据元素和两个指针域,其中指针域可能为 NULL。如果指针域为 NULL,则表示该节点没有对应的左/右子节点。

考虑如何创建一个简单的二叉树,如下图所示:

        10
       /  \
      5   15
         /  \
        12  20

可以使用以下 C++ 代码构建此二叉树:

BinaryTreeNode* root = new BinaryTreeNode {10, nullptr, nullptr};
root->left_child = new BinaryTreeNode {5, nullptr, nullptr};
root->right_child = new BinaryTreeNode {15, nullptr, nullptr};
root->right_child->left_child = new BinaryTreeNode {12, nullptr, nullptr};
root->right_child->right_child = new BinaryTreeNode {20, nullptr, nullptr};

这里通过 new 运算符动态创建每个节点,并在需要时设置其左右子节点指针。

二叉链表存储结构的优点是可以很方便地遍历二叉树,例如使用递归实现前序、中序和后序遍历。由于每个节点有两个指针域,因此需要额外的空间来存储这些指针。此外,二叉链表存储结构相对于顺序存储结构,插入和删除操作更为高效。

除了前、中、后序遍历之外,二叉树的二叉链表存储结构还允许进行其他类型的遍历,例如层次遍历和镜像遍历。

层次遍历是一种广度优先搜索(BFS)的算法,按照从上到下、从左到右的顺序依次访问每个节点。层次遍历可以使用队列来实现,将根节点加入队列,然后逐层遍历其子节点。对于当前访问的节点,首先将其子节点加入队列,然后出队队首节点继续访问。直到队列为空,遍历结束。

下面是 C++ 实现层次遍历的代码:

void level_order_traversal(BinaryTreeNode* root) {
    if (root == nullptr) return;
    queue<BinaryTreeNode*> q;
    q.push(root);
    while (!q.empty()) {
        auto current = q.front();
        q.pop();
        cout << current->data << " ";
        if (current->left_child != nullptr) q.push(current->left_child);
        if (current->right_child != nullptr) q.push(current->right_child);
    }
}

镜像遍历(Mirror Traversal)是指访问二叉树的一个镜像,也就是先访问右子树再访问左子树。镜像遍历可以利用递归实现,并将左右子树的访问顺序交换即可。

下面是 C++ 实现镜像遍历的代码:

void mirror_traversal(BinaryTreeNode* root) {
    if (root == nullptr) return;
    mirror_traversal(root->right_child);
    cout << root->data << " ";
    mirror_traversal(root->left_child);
}

二叉树的二叉链表存储结构可以方便地实现这些遍历算法,并提供了高效的插入和删除操作。在使用二叉树时,可以根据具体场景选择适合的存储结构,以达到更好的性能和实现效果。

   1
  / \
 2   3
    / \
   4   5
  /
 6

二叉链存储结构

什么是二叉链存储结构

二叉链存储结构是一种常用的表示二叉树的存储方式,它使用节点对象和引用来表示二叉树的结构。

它将每个节点分别表示为一个包含该节点的数据、一个指向其左子树的指针、一个指向其右子树的指针以及一个指向其父节点的指针的结构体。这样,每个节点可以通过其左右子树的指针进行遍历和访问,同时也可以通过其父节点的指针追溯到其祖先节点。

二叉链存储结构相对于其他二叉树存储结构的优点在于,可以方便地实现一些操作,例如:给定一个节点,可以快速地找到其父节点;给定两个节点,可以快速地计算它们之间的距离(即它们的最近公共祖先到它们的距离之和)。

在使用二叉链存储结构时,由于每个节点都包含其父节点的指针,因此需要额外的空间开销。同时,为了避免出现环形引用,通常会将根节点的父节点指针设置为 NULL 。

以下是一个简单的 C++ 实现,包括二叉链存储结构的定义、建立二叉树、遍历、统计节点数、求二叉树高度和打印二叉树的操作。

#include <iostream>
using namespace std;

// 定义二叉树的节点结构
struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

// 建立二叉树
TreeNode* createBinaryTree(string s, int& index) {
    if (index >= s.length()) {
        return NULL;
    }
    if (s[index] == '#') {
        index++;
        return NULL;
    }
    TreeNode* root = new TreeNode(s[index] - '0');
    index++;
    root->left = createBinaryTree(s, index);
    root->right = createBinaryTree(s, index);
    return root;
}

// 先序遍历
void preorderTraversal(TreeNode* root) {
    if (root) {
        cout << root->val << " ";
        preorderTraversal(root->left);
        preorderTraversal(root->right);
    }
}

// 统计节点数
int countNodes(TreeNode* root) {
    if (root == NULL) {
        return 0;
    }
    return 1 + countNodes(root->left) + countNodes(root->right);
}

// 求二叉树高度
int getHeight(TreeNode* root) {
    if (root == NULL) {
        return 0;
    }
    int leftHeight = getHeight(root->left);
    int rightHeight = getHeight(root->right);
    return max(leftHeight, rightHeight) + 1;
}

// 打印二叉树
void printBinaryTree(TreeNode* root, int level) {
    if (root == NULL) {
        return;
    }
    printBinaryTree(root->right, level + 1);
    for (int i = 0; i < level; i++) {
        cout << "    ";
    }
    cout << root->val << endl;
    printBinaryTree(root->left, level + 1);
}

int main() {
    string s = "123##4#6##5##";
    int index = 0;
    TreeNode* root = createBinaryTree(s, index);
    
    // 测试先序遍历
    cout << "Preorder traversal: ";
    preorderTraversal(root);
    cout << endl;
    
    // 测试统计节点数
    cout << "Number of nodes: " << countNodes(root) << endl;
    
    // 测试求二叉树高度
    cout << "Height of the binary tree: " << getHeight(root) << endl;
    
    // 测试打印二叉树
    cout << "Print the binary tree:" << endl;
    printBinaryTree(root, 0);
    
    return 0;
}

意见和建议

这段代码的实现有一些问题和可以改进的地方:

  1. 输入校验不足:代码中对输入的字符串格式没有进行校验,如果输入的字符串不符合预期的二叉树表示形式,可能会导致程序出错。

  2. 没有内存释放:在创建二叉树的过程中使用了 new 关键字来动态分配内存,但在程序结束时没有释放这些内存,可能会导致内存泄漏。

  3. 打印二叉树的格式化问题:当前的打印函数输出的二叉树结构不够美观,可以考虑使用更好的格式化方法来打印二叉树,使其更易于理解。

  4. 缺乏错误处理:代码中没有对可能出现的错误情况进行处理,比如在创建二叉树过程中发生内存分配失败,或者输入的字符串格式不正确时,程序没有提供相应的异常处理。

  5. 全局变量的使用:代码中使用了全局变量 index 来记录当前处理的字符位置,这种做法不利于代码的可维护性和可移植性。

建议改进的地方包括:

  1. 增加输入校验:在创建二叉树的过程中,可以增加对输入字符串格式的校验,确保输入的字符串能够正确表示一个二叉树。

  2. 添加内存释放:在程序结束时,应该释放通过 new 分配的内存,避免内存泄漏问题。

  3. 优化打印函数:可以考虑使用更好的方法来打印二叉树,例如按层级打印,或者使用图形化的方式呈现二叉树结构。

  4. 增加错误处理:对可能出现的错误情况进行处理,比如在内存分配失败时给出相应的提示,或者对输入格式不正确的情况进行处理。

  5. 减少全局变量的使用:避免使用全局变量,尽量将变量的作用域限制在函数内部,以提高代码的可维护性和可读性。

综上所述,对这段代码的改进包括增加输入校验、添加内存释放、优化打印函数、增加错误处理和减少全局变量的使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1210492.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kibana:使用 “链接” 面板简化 Kibana 仪表板导航 - Links panel

作者&#xff1a;Teresa Alvarez Soler 我们很高兴地宣布 Kibana 仪表板的最新功能版本&#xff1a;链接面板&#xff08;Links panel&#xff09;&#xff0c;这是在仪表板之间组织和导航的简单方法。 此功能在 Kibana 8.11 的技术预览版中提供。 有时你可能希望创建多个主题…

μC/OS-II---计时器管理1(os_tmr.c)

目录 创建一个计时器重新启动一个计时器停止一个计时器删除一个计时器 计时器是倒计时器&#xff0c;当计数器达到零时执行某个动作。用户通过回调函数提供这个动作。回调函数是用户声明的函数&#xff0c;在计时器到期时被调用。在回调函数中绝对不能进行阻塞调用&#xff08;…

SUMO道路封闭车辆绕行仿真实验【TraCI】

本文将介绍如何在 SUMO 交通模拟中动态选择车辆绕行指定道路。 绕道是城市驾驶中的常见现象&#xff0c;造成原因有很多&#xff0c;包括建筑和交通事故等。 无论出于何种原因&#xff0c;并非所有车辆都会选择避开这些道路&#xff1b; 有些人可能会毫不犹豫地直接开车过去&a…

Payshield 10K是什么意思?有什么作用?

PayShield 10K是一种支付安全产品&#xff0c;由数字货币和法币混合而成的数字货币产品。它的意思是保护商家在交易过程中可能遭受的损失。这种产品的主要作用是保护数字货币支付系统的安全&#xff0c;并确保商家在交易过程中获得他们应得的收益。 PayShield 10K具有以下特点和…

基于鸟群算法优化概率神经网络PNN的分类预测 - 附代码

基于鸟群算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于鸟群算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于鸟群优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神经网络的光滑…

Domino为外出Internet邮件设置DKIM签名

大家好&#xff0c;才是真的好。 如果你看了上篇《Domino中和邮件安全有关的SPF、DKIM介绍》内容&#xff0c;想必就对DKIM概念不陌生&#xff0c;当然&#xff0c;上篇我们讲的是邮件入站的SFP、DKIM签名检查&#xff0c;这篇讲述的是外出邮件的DKIM签名。 是的&#xff0c;…

算不上最全,但都是必备——Spring MVC这些不会不行啊

Spring MVC篇 Spring MVC执行流程 四大组件 前端控制器DispatcherServlet处理器映射器HandlerMapping处理器适配器HandlerAdaptor视图解析器ViewResolver 视图阶段&#xff08;JSP&#xff09; 请求先到前端控制器DispatcherServlet DispatcherServlet将根据该请求的路径去…

理解 R-CNN:目标检测的一场革命

一、介绍 对象检测是一项基本的计算机视觉任务&#xff0c;涉及定位和识别图像或视频中的对象。多年来&#xff0c;人们开发了多种方法来应对这一挑战&#xff0c;但基于区域的卷积神经网络&#xff08;R-CNN&#xff09;的发展标志着目标检测领域的重大突破。R-CNN 及其后续变…

深入探讨Linux中的文本文件查看命令

目录 前言1 cat命令2 less命令3 more命令4 head命令5 tail命令6 总结 前言 在Linux系统中&#xff0c;文本文件是日常工作中不可或缺的一部分&#xff0c;无论是配置文件、日志文件还是代码文件&#xff0c;都需要用到文本文件查看命令。在本文中&#xff0c;我们将深入研究一…

【深度学习】吴恩达课程笔记(四)——优化算法

笔记为自我总结整理的学习笔记&#xff0c;若有错误欢迎指出哟~ 【吴恩达课程笔记专栏】 【深度学习】吴恩达课程笔记(一)——深度学习概论、神经网络基础 【深度学习】吴恩达课程笔记(二)——浅层神经网络、深层神经网络 【深度学习】吴恩达课程笔记(三)——参数VS超参数、深度…

如何从 iCloud 恢复永久删除的照片?答案在这里!

在数字时代&#xff0c;丢失珍贵的照片可能会令人痛苦。然而&#xff0c;了解如何从 iCloud 恢复永久删除的照片可以带来一线希望。无论是意外删除还是技术故障&#xff0c;本指南都提供了 2023 年的最新方法来找回您的珍贵记忆。发现分步解决方案并轻松重新访问您的照片库。不…

智能供应链中的预测算法:理论与实践

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 引言 智能供应链已经成…

制作属于你的视觉小说,ComfyUI工作流#N3期AIGC训练营

什么是视觉小说&#xff1f; Visual Novel 最初这种形式被称为“有声小说” 视觉小说是一种源自日本的电子游戏类型&#xff0c;它以图像和文本为主要表现形式&#xff0c;通常包含大量的对话和故事情节。 &#xff08;大量对话&#xff09; 在视觉小说中&#xff0c;玩家可以通…

AJAX入门Day01笔记

Day01_Ajax入门 知识点自测 如下对象取值的方式哪个正确? let obj {name: 黑马 }A: obj.a B: obj()a 答案 A选项正确 哪个赋值会让浏览器解析成标签显示? let ul document.querySelector(#ul) let str <span>我是span标签</span>A: ul.innerText str B: ul…

HTML+CSS+JavaScript实战(一个简易的视频播放器)

效果如下&#xff1a; 思路很常规&#xff0c;无需注释即可看懂&#xff08;其实是懒得敲 bushi&#xff09; 没有注释也能跑&#xff0c;so直接上源码~ 感谢 夏柔站长 提供的免费API index.html <!DOCTYPE html> <html lang"en"> <head><meta …

UE4动作游戏实例RPG Action解析三:实现效果,三连击Combo,射线检测,显示血条,火球术

一、三连Combo 实现武器三连击,要求: 1.下一段Combo可以随机选择, 2.在一定的时机才能再次检测输入 3. 等当前片段播放完才播放下一片段 1.1、蒙太奇设置 通过右键-新建蒙太奇片段,在蒙太奇里创建三个片段,并且移除相关连接,这样默认只会播放第一个片段 不同片段播…

一分钟搞懂什么是this指针(未涉及静态成员和函数)

前言 我们在学习类的过程中&#xff0c;一定听说过this指针&#xff0c;但是并不知道它跟谁相似&#xff0c;又有什么用途&#xff0c;所以接下来&#xff0c;让我们一起去学习this指针吧&#xff01; 一、this指针的引入 我们先来看下面两段代码&#xff0c;它们输出的是什么&…

Rust实战教程:构建您的第一个应用

大家好&#xff01;我是lincyang。 今天&#xff0c;我们将一起动手实践&#xff0c;通过构建一个简单的Rust应用来深入理解这门语言。 我们的项目是一个命令行文本文件分析器&#xff0c;它不仅能读取和显示文件内容&#xff0c;还会提供一些基础的文本分析&#xff0c;如计算…

C# Onnx 轻量实时的M-LSD直线检测

目录 介绍 效果 效果1 效果2 效果3 效果4 模型信息 项目 代码 下载 其他 介绍 github地址&#xff1a;https://github.com/navervision/mlsd M-LSD: Towards Light-weight and Real-time Line Segment Detection Official Tensorflow implementation of "M-…

什么是Vue.js中的单向数据流(one-way data flow)?为什么它重要?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…