数据结构:图文详解 树与二叉树(树与二叉树的概念和性质,存储,遍历)


目录

一.树的概念

二.树中重要的概念

三.二叉树的概念

满二叉树

完全二叉树

四.二叉树的性质

五.二叉树的存储

六.二叉树的遍历

前序遍历

中序遍历 

后序遍历 


一.树的概念

树是一种非线性数据结构,它由节点和边组成。树的每个节点可以有零个或多个子节点,其中一个节点被指定为根节点。树的节点之间通过边连接。另外,树形结构中,子树之间不能有交集,否则就不是树形结构。

树的结构具有层级关系,根节点位于最顶层,而叶节点位于最底层。树的形状可以类比于现实生活中的树,根节点相当于树的根部,而分支和叶子节点则相当于树的枝干和叶子。

在计算机科学中,树被广泛用于各种应用,例如文件系统、数据库索引、编译器中的抽象语法树等。树的常见特点是具有唯一的根节点、没有循环的边、可以有任意数量的子节点等。

树的常见操作包括插入新节点、删除节点、查找节点、遍历节点等。常见的树结构包括二叉树、红黑树、AVL树等。树的设计和使用在算法和数据结构领域中非常重要,它可以提供高效的数据存储和检索方式。 


二.树中重要的概念

笔者就以下图作为例子进行说明:

  • 结点的度:一个结点含有子树的个数称为该结点的度; 如上图:A的度为6
  • 树的度:一棵树中,所有结点度的最大值称为树的度; 如上图:树的度为6
  • 叶子结点或终端结点:度为0的结点称为叶结点; 如上图:B、C、H、I...等节点为叶结点
  • 双亲结点或父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点; 如上图:A是B的父结点
  • 孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点; 如上图:B是A的孩子结点
  • 根结点:一棵树中,没有双亲结点的结点;如上图:A
  • 结点的层次:从根开始定义起,根为第1层,根的子结点为第2层,以此类推
  • 树的高度或深度:树中结点的最大层次; 如上图:树的高度为4
  • 非终端结点或分支结点:度不为0的结点; 如上图:D、E、F、G...等节点为分支结点
  • 兄弟结点:具有相同父结点的结点互称为兄弟结点; 如上图:B、C是兄弟结点
  • 堂兄弟结点:双亲在同一层的结点互为堂兄弟;如上图:H、I互为兄弟结点
  • 结点的祖先:从根到该结点所经分支上的所有结点;如上图:A是所有结点的祖先
  • 子孙:以某结点为根的子树中任一结点都称为该结点的子孙。如上图:所有结点都是A的子孙
  • 森林:由m(m>=0)棵互不相交的树组成的集合称为森林 

三.二叉树的概念

二叉树是一种常见的树状数据结构,在二叉树中,每个节点最多有两个子节点,分别称为左子节点右子节点。二叉树的特点是每个节点最多只有两个子节点,且子节点的位置是有序的,即左子节点在右子节点之前。

二叉树可以为空,如果一个二叉树不为空,则它一定由根节点左子树右子树组成。每个节点都有一个值,可以是任意类型的数据。

二叉树也可以只有一个节点,如下图所示,根节点不为空,但是左子树和右子树为空,这样的二叉树也是允许存在的

总的来说,对于任意一颗二叉树,它都是由以下几部分复合而成的

二叉树可以用来表示许多实际问题,例如计算机科学中的排序和搜索算法。在二叉树中,有一些特殊的类型,如满二叉树、完全二叉树和平衡二叉树等。二叉树还可以通过遍历算法来访问其中的节点,最常见的遍历算法有前序遍历、中序遍历和后序遍历。

二叉树中还有以下俩个常见的特殊的二叉树

满二叉树

一棵二叉树,如果每层的结点数都达到最大值,则这棵二叉树就是满二叉树。也就是说,如果一棵二叉树的层数为 k ,且结点总数是 2^{k}-1,则它就是满二叉树。

完全二叉树

完全二叉树是由满二叉树而引出来的,对于深度为 k 的,有 n 个结点的二叉树,当且仅当其每一个结点都与深度为 k 的满二叉树中编号从0n-1的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树


四.二叉树的性质

  1. 若规定根结点的层数为1,则一棵非空二叉树的第 i 层上最多有2^{i-1}个节点
  2. 若规定只有根结点的二叉树的深度为,则深度为K的二叉树的最大结点数是2^{k}-1
  3. 对任何一棵二叉树, 如果其叶结点个数为 n0 , 度为2的非叶结点个数为 n2 ,则有 n0=n2+1 
  4.  具有 n 个结点的完全二叉树的深度 k \log _{2}^{}\textrm{}(n+1)
  5. 对于具有 n 个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为 i 的结点有:若i>0,双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点;若2i+1<n,左孩子序号:2i+1,否则无左孩子;若2i+2<n,右孩子序号:2i+2,否则无右孩

五.二叉树的存储

对于任意一个节点,我们最多只能知道它的左右孩子节点和根节点,以及自己保存的数据,由此我们引申出许多种表示二叉树的方法,常见的有孩子表示法孩子双亲表示法兄弟节点表示法...

// 孩子表示法
class Node {
    int val; // 数据域
    Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树
    Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
}

// 孩子双亲表示法
class Node {
    int val; // 数据域
    Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树
    Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
    Node parent; // 当前节点的根节点
}

笔者以下图举例:

 

我们使用孩子表示法,然后依次按照图示组成一颗二叉树(后文的遍历都是基于此树)

public class MyBinaryTree {
    public class TreeNode {
        public char val;//记录当前节点的值
        public TreeNode leftNode;//记录当前节点的左孩子节点
        public TreeNode rightNode;//记录当前节点的右孩子节点
        public TreeNode(char val) {//初始化节点的值
            this.val = val;
        }
    }
    //生成每一个节点,然后连起来
    public TreeNode CreatTree() {
        TreeNode A = new TreeNode('A');
        TreeNode B = new TreeNode('B');
        TreeNode C = new TreeNode('C');
        TreeNode D = new TreeNode('D');
        TreeNode E = new TreeNode('E');
        TreeNode F = new TreeNode('F');
        TreeNode G = new TreeNode('G');
        TreeNode H = new TreeNode('H');
        //依次按照图示连起来
        A.leftNode = B;
        A.rightNode = C;
        B.leftNode = D;
        C.leftNode = E;
        C.rightNode = F;
        //最后返回根节点
        return A;
    }
}

六.二叉树的遍历

二叉树的遍历是指按照一定的规则,将二叉树中的所有节点访问一次,并且每个节点只访问一次。常见的二叉树遍历方式有前序遍历中序遍历后序遍历

前序遍历

前序遍历(preorder traversal):先访问根节点,然后遍历左子树,最后遍历右子树。具体步骤如下:

  1. 访问根节点
  2. 前序遍历左子树
  3. 前序遍历右子树 

代码实现如下:

    //前序遍历
    public void preOrder(TreeNode root) {
        if (root == null) {
            return;//空树不需要遍历
        }
        System.out.print(root.val + " ");//访问根节点
        preOrder(root.leftNode);//前序遍历左子树
        preOrder(root.rightNode);//前序遍历右子树
    }

对于上述的代码,程序运行起来的具体逻辑是如下图这样的,反复的递归和回退进行实现

 

中序遍历 

中序遍历(inorder traversal):先遍历左子树,然后访问根节点,最后遍历右子树。具体步骤如下:

  1. 中序遍历左子树
  2. 访问根节点
  3. 中序遍历右子树

代码实现如下:

    //中序遍历
    public void inOrder(TreeNode root) {
        if (root == null) {
            return;//空树不需要遍历
        }
        inOrder(root.leftNode);//中序遍历左子树
        System.out.print(root.val + " ");//访问根节点
        inOrder(root.rightNode);//中序遍历右子树
    }

后序遍历 

后序遍历(postorder traversal):先遍历左子树,然后遍历右子树,最后访问根节点。具体步骤如下:

  1. 后序遍历左子树
  2. 后序遍历右子树
  3. 访问根节点

代码实现如下:

    //后序遍历
    public void postOrder(TreeNode root) {
        if (root == null) {
            return;//空树不需要遍历
        }
        postOrder(root.leftNode);//后序遍历左子树
        postOrder(root.rightNode);//后序遍历右子树
        System.out.print(root.val + " ");//访问根节点
    }

我们可以写个测试来看看遍历的结果:

public class Test {
    public static void main(String[] args) {
        MyBinaryTree myBinaryTree = new MyBinaryTree();
        MyBinaryTree.TreeNode rootNode = myBinaryTree.CreatTree();
        
        System.out.println();
        System.out.println("前序遍历:");
        myBinaryTree.preOrder(rootNode);
        
        System.out.println();
        System.out.println("中序遍历:");
        myBinaryTree.inOrder(rootNode);
        
        System.out.println();
        System.out.println("后序遍历:");
        myBinaryTree.postOrder(rootNode);
    }
}

输出:

也就是说:

其实不管是前序,中序,后序遍历,他们整体的搜索过程都是一样的,不同的地方在于对当前根节点的处理时间不一样:前序遍历是先处理根节点;中序遍历是先遍历当前节点的左子树再处理当前根节点;而后序遍历则是最后再处理根节点,就像下图一样

以上是三种最基本的二叉树遍历方式。除了这三种,还有一些其他的二叉树遍历方式,比如层序遍历螺旋遍历等。不同的遍历方式适用于不同的场景和问题,可以根据具体的需求选择合适的遍历方式。




  本次的分享就到此为止了,希望我的分享能给您带来帮助,也欢迎大家三连支持,你们的点赞就是博主更新最大的动力!如有不同意见,欢迎评论区积极讨论交流,让我们一起学习进步!有相关问题也可以私信博主,评论区和私信都会认真查看的,我们下次再见

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

输入两个时间,判断时间是否为非工作日,并且是日期否为同一天。是的话返回true,否返回false

工作遇到这么一个逻辑&#xff0c;前端回传两个时间&#xff08;必须是两个那一种&#xff09;。然后&#xff0c;我后端需要判断这两个时间是否为同一天&#xff0c;并且这个时间是否为非工作日&#xff0c;是的话返回true&#xff0c;反之返回false 代码&#xff1a; packa…

2023/12/26 work

1. 定义自己的命名空间myspace&#xff0c;并在myspace中定义一个字符串&#xff0c;实现求字符串大小的函数。 #include <iostream>using namespace std;namespace mynamespace {string name;unsigned int strlen(string name){return name.size();} }using namespace…

面试题之二HTTP和RPC的区别?

面试题之二 HTTP和RPC的区别&#xff1f; Ask范围&#xff1a;分布式和微服务 难度指数&#xff1a;4星 考察频率&#xff1a;70-80% 开发年限&#xff1a;3年左右 从三个方面来回答该问题&#xff1a; 一.功能特性 1)HTTP是属于应用层的协议&#xff1a;超文本传输协议…

手机蓝牙在物联网超市中的应用

超市一站式购物已进入城市的千家万户。然而人们在选购时却采用直接翻阅商品的方式&#xff0c;既不方便又不卫生甚至大大缩短食品类商品保质期&#xff0c;也给超市商品管理造成很大难度。物联网(The Internet of things)基于射频识别(RFID)、红外感应等技术&#xff0c;把物品…

【PostGIS】在Java中操作postgis——使用springboot+Maven+mybatis框架

前言&#xff1a; PostgreSQL15对应PostGIS安装教程及空间数据可视化 空间数据库-常用空间函数 完成PostGIS的安装与配置后&#xff0c;让我们来写一个Java操作postgis数据库的demo吧~ 使用工具&#xff1a; NavicatIDEA 一、PostGIS数据库准备 在Navicat中新建一个postgr…

云渲染UE4像素流送搭建(winows、ubuntu单实例与多实例像素流送)

windows/ubuntu20.4下UE4.27.2像素流送 像素流送技术可以将服务器端打包的虚幻引擎应用程序在客户端的浏览器上运行&#xff0c;用户可以通过浏览器操作虚幻引擎应用程序&#xff0c;客户端无需下载虚幻引擎&#xff0c;本文实现两台机器通过物理介质网线实现虚幻引擎应用程序…

Golang 协程配合管道

请完成goroutine和channel协同工作的案例&#xff0c;具体要求&#xff1a; &#xff08;1&#xff09;开启一个writeData协程&#xff0c;向管道mtChan中写入50个整数. &#xff08;2&#xff09;开启一个readData协程&#xff0c;从管道intChan中读取writeData写入的数据。 &…

系列十(实战)、发送 接收批量消息(Java操作RocketMQ)

一、发送 & 接收批量消息 1.1、概述 批量消息是指RocketMQ可以把一组消息集合一次性发送&#xff0c;这一组消息会被当做一个消息供消费者消费。 1.2、Demo05MQTestApp /*** Author : 一叶浮萍归大海* Date: 2023/12/25 11:48* Description: 发送 & 接收批量消息*/ …

智能优化算法应用:基于袋獾算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于袋獾算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于袋獾算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.袋獾算法4.实验参数设定5.算法结果6.参考文献7.MA…

python CodeFormer 图像(人脸面部)修复源码

介绍 github地址&#xff1a;https://github.com/sczhou/CodeFormer [NeurIPS 2022] Towards Robust Blind Face Restoration with Codebook Lookup Transformer 效果&#xff1a; 测试环境&#xff1a; anconda3python3.8 torch1.9.0cu111 pyqt5 部分代码&#xff1a; i…

记一次应急响应练习(windows)

记一次应急响应练习&#xff08;windows&#xff09; windows&#xff1a; 1.请提交攻击者攻击成功的第一时间&#xff0c;格式&#xff1a;YY:MM:DD hh:mm:ss 答&#xff1a;2023/04/29:22:44:32 思路&#xff1a; 看见桌面的小皮面板&#xff0c;进入小皮的安装目录。发现…

nodejs进阶

文章目录 写在前面一、dependencies、devDependencies和peerDependencies区别&#xff1a;二、需要牢记的npm命令2.1 npm init2.2 npm config list2.3 npm配置镜像源 三、npm install 的原理四、package-lock.json的作用五、npm run 的原理六、npx6.1 npx是什么6.2 npx的优势6.…

一个卖美妆的 一个月招了数十万代理!月销售额破亿 你敢相信吗?

商业模式永不过时 大家好&#xff0c;我是吴军&#xff0c;一家软件公司的产品经理 今天我们来聊一下这个纪炫商城 其实&#xff0c;说这个纪炫商城之前&#xff0c;我想跟各位企业家老板聊几句实在话 作为公司两百多号技术的&#xff0c;一个拥有五年软件开发经验的产品经理…

深入探讨Java反射:解析机制与应用场景

当谈及Java编程语言的强大功能时&#xff0c;反射&#xff08;Reflection&#xff09;是一个不可忽视的特性。反射允许程序在运行时检查和操作其自身的结构&#xff0c;这为开发者提供了一种动态获取信息和执行操作的途径。在本篇博客中&#xff0c;我们将深入探讨Java反射的原…

分支限界法求解01背包(优先队列)【java】

实验内容&#xff1a;运用分支限界法解决0-1背包问题 实验目的&#xff1a;分支限界法按广度优先策略遍历问题的解空间树&#xff0c;在遍历过程中,对已经处理的每一个结点根据限界函数估算目标函数的可能取值&#xff0c;从中选取使目标函数取得极值的结点优先进行广度忧先搜…

day42 1226

作业1&#xff1a; #include <iostream>using namespace std;namespace myspace {string str; }int length(string str) {//char *p &str.at(0);const char *p str.data();int count 0;while (*p ! 0) {p;count;}return count; } int main() {getline(cin,myspac…

元素隐式具有 “any“ 类型,因为类型为 “string“ 的表达式不能用于索引类型 “typeof

报错展示 解决办法 Object.keys(directives).forEach(k > {app.directive(k, directives[k as keyof typeof directives]) })

工具系列:TensorFlow决策森林_(3)使用dtreeviz可视化

文章目录 介绍设置安装 TF-DF 和 dtreeviz导入库 可视化分类树加载、清洗和准备数据分割训练/测试集并训练模型训练一个随机森林分类器显示决策树检查叶节点统计信息决策树如何对实例进行分类特征空间划分 可视化回归树加载、清洗和准备数据分割训练/测试集并训练模型训练一个随…

【linux】线程概念

线程概念 1.储备知识1.1再谈页表 2.线程概念2.1如何理解多线程2.2如何证明2.3什么是线程2.4线程的优点2.4线程的缺点2.5线程异常2.6进程vs线程 喜欢的点赞&#xff0c;收藏&#xff0c;关注一下把&#xff01; 1.储备知识 1.1再谈页表 在上一篇博客说过&#xff0c;页表除了用…