数据结构--树
一、树的基本术语
结点:树中的一个独立单元
结点的度:结点下分支的个数
树的度:树中所有结点中度的最大值
非终端结点:度不为0的结点
双亲和孩子:结点下的子树称为该结点的孩子.相应地,该结点称为孩子的双亲
兄弟:同一个双亲的孩子之间
祖先:从根到该结点所经分支上的所有结点
子孙:从某结点为根,其下直接或间接的结点
层次:从根开始,根的孩子为第二层,依次类推
堂兄弟:双亲在同一层的结点
树的深度:树中结点的最大层次称为树的深度或高度
有序树和无序树:如果将树中所有结点的各子树看成从左至右是有次序的,反之无序的为无序树(在有序树中最左边的子树的根称为第一个孩子最右边称为最后一个孩子)
森林m(m>=0)棵互不相交的树的集合
二、二叉树(Binary Tree)
1>有且仅有一个称为根的结点
2>除叶子结点,剩余每个结点可能有左孩子或右孩子(最多有两个)
二叉树具有天然递归结构
每个结点的左子树也是二叉树
每个结点的右子树也是二叉树
性质1:在二叉树的第i层上至多有2^(i-1)个结点(i>=1)
性质2:深度为k的二叉树至多有2^k-1个结点(k>=1)
性质3:对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0= n2+1(度为0的结点数比度为2的结点数多1)
三、二分搜索树
二分搜索树是二叉树
二分搜索树的每个结点的值(大于其左子树的所有结点的值|小于其右子树的所有结点的值)
每一棵子树也是二分搜索树
二分搜索树的相关操作
创建二分搜索树的类
// 保存在树中的结点必须具有可比性
public class BinearySearchTree<T extends Comparable<T>> {}
创建树的结点
private class Node<T> {private T ele;// 结点的值private int frequence;// 频率private Node<T> left, right;// 分别指向左右孩子的索引public Node(T ele) {this.ele = ele;this.frequence = 1;this.right = this.left = null;}}
树对应的属性
private Node<T> root;// 树的根节点private int size;// 书中结点的个数
构建树
public BinearySearchTree() {this.root = null;this.size = 0;}
获取树中结点数
public int getSize() {return this.size;}
向树中添加结点
public void add(T ele) {// 更新根节点this.root = addDG(this.root, ele);}// 语义:向以root为根的二分搜索树中添加元素eleprivate Node addDG(Node<T> root, T ele) {// 递归终止条件if (root == null) {this.size++;return new Node<T>(ele);}// 递归操作if (root.ele.compareTo(ele) > 0) {root.left = addDG(root.left, ele);} else if (root.ele.compareTo(ele) < 0) {root.right = addDG(root.right, ele);} else {// 更新频率root.frequence++;}return root;}
查询某个值在树中是否存在
public boolean search(T ele) {return searchDG(this.root, ele);}// 语义:从以root为根的二分搜索树中查找元素eleprivate boolean searchDG(Node<T> root, T ele) {// 递归终止条件if (root == null) {return false;}// 递归操作if (root.ele.compareTo(ele) == 0) {return true;} else if (root.ele.compareTo(ele) > 0) {return searchDG(root.left, ele);} else {return searchDG(root.right, ele);}}
二分搜索树的先序遍历
public void preTravel() {List<AbstractMap.SimpleEntry<T, Integer>> list = new ArrayList<>();preTravelDG(this.root, list);String resStr = list.stream().map(item -> "[" + item.getKey() + ":" + item.getValue() + "]").collect(Collectors.joining("-"));System.out.println(resStr);}// 先序遍历以root为根的树,将结果保存在list中private void preTravelDG(Node<T> root, List<AbstractMap.SimpleEntry<T, Integer>> list) {// 递归终止条件if (root == null) {return;}// 递归操作list.add(new AbstractMap.SimpleEntry<>(root.ele, root.frequence));// 遍历左子树preTravelDG(root.left, list);// 遍历右子树preTravelDG(root.right, list);}
二分搜索树的中序遍历
public void midTravel() {List<AbstractMap.SimpleEntry<T, Integer>> list = new ArrayList<>();midTravelDG(this.root, list);String resStr = list.stream().map(item -> "[" + item.getKey() + ":" + item.getValue() + "]").collect(Collectors.joining("-"));System.out.println(resStr);}// 中序遍历以root为根的树,将结果保存在list中private void midTravelDG(Node<T> root, List<AbstractMap.SimpleEntry<T, Integer>> list) {// 递归终止条件if (root == null) {return;}// 递归操作// 遍历左子树midTravelDG(root.left, list);list.add(new AbstractMap.SimpleEntry<>(root.ele, root.frequence));// 遍历右子树midTravelDG(root.right, list);}
二分搜索树的后序遍历
public void sufTravel() {List<AbstractMap.SimpleEntry<T, Integer>> list = new ArrayList<>();sufTravelDG(this.root, list);String resStr = list.stream().map(item -> "[" + item.getKey() + ":" + item.getValue() + "]").collect(Collectors.joining("-"));System.out.println(resStr);}// 后序遍历以root为根的树,将结果保存在list中private void sufTravelDG(Node<T> root, List<AbstractMap.SimpleEntry<T, Integer>> list) {// 递归终止条件if (root == null) {return;}// 递归操作// 遍历左子树sufTravelDG(root.left, list);// 遍历右子树sufTravelDG(root.right, list);list.add(new AbstractMap.SimpleEntry<>(root.ele, root.frequence));}
层序遍历(依赖队列)
public void levelTravel() {// 判断树是否为空if (this.isEmpty()) {return;}Queue<AbstractMap.SimpleEntry<Node<T>, Integer>> queue = new LinkedList<>();// 1.现将根节点入队queue.add(new AbstractMap.SimpleEntry<>(this.root, 1));// 2.遍历队列while (!queue.isEmpty()) {// 2-1 出队AbstractMap.SimpleEntry<Node<T>, Integer> pair = queue.poll();// 结点Node<T> node = pair.getKey();// 层int level = pair.getValue();System.out.println("[val:" + node.ele + ",level:" + level + "]");// 2-2 判断左右子树是否为空if (node.left != null) {queue.add(new AbstractMap.SimpleEntry<>(node.left, level + 1));}if (node.right != null) {queue.add(new AbstractMap.SimpleEntry<>(node.right, level + 1));}}}
判断是否为空
public boolean isEmpty() {return this.size == 0;}
寻找树中最小元素
public T getMinValue() {if (this.isEmpty()) {return null;}Optional<Node<T>> optional = getMinNode(this.root);return optional.get().ele;}// 语义:在以Node为根结点的树中查找最小结点private Optional<Node<T>> getMinNode(Node<T> node) {// 递归终止的条件if (node.left == null) {return Optional.of(node);}// 递归操作return getMinNode(node.left);}
寻找树中最大元素
public T getMaxValue() {if (this.isEmpty()) {return null;}Optional<Node<T>> optional = getMaxNode(this.root);return optional.get().ele;}// 语义:在以Node为根结点的树中查找最大结点private Optional<Node<T>> getMaxNode(Node<T> node) {// 递归终止的条件if (node.right == null) {return Optional.of(node);}// 递归操作return getMaxNode(node.right);}
删除最小结点
public T removeMinNode() {T result = getMinValue();if (result == null) {return null;}// 更新根节点this.root = removeMinNode(this.root);return result;}/*** 语义:从以node为根的二分搜索树中删除元素最小的结点** @param node 树的根结点* return 删除最小结点之后的树的根结点*/private Node<T> removeMinNode(Node<T> node) {// 递归终止条件if (node.left == null) {// 删除操作// 1.记录右子树Node<T> rightTree = node.right;// 2.失去关联关系node.right = null;// 3.更新sizethis.size--;return rightTree;}// 递归操作node.left = removeMinNode(node.left);return node;}
删除最大结点
public T removeMaxNode() {T result = getMaxValue();if (result == null) {return null;}// 更新根节点this.root = removeMaxNode(this.root);return result;}/*** 语义:从以node为根的二分搜索树中删除元素最大的结点** @param node 树的根结点* return 删除最大结点之后的树的根结点*/private Node<T> removeMaxNode(Node<T> node) {// 递归终止条件if (node.right == null) {// 删除操作// 1.记录左子树Node<T> leftTree = node.left;// 2.失去关联关系node.left = null;// 3.更新sizethis.size--;return leftTree;}// 递归操作node.right = removeMaxNode(node.right);return node;}
删除任意结点
public void remove(T ele) {// 根据值查找结点this.root = remove(this.root, ele);}private Node<T> remove(Node<T> node, T ele) {// 递归终止条件// 没有找到的情况if (node == null) {return null;}// 找到了删除的结点if (node.ele.compareTo(ele) == 0) {this.size--;// node就是要删除的结点 // 1.左子树空,右子树不为空if (node.left == null) {Node<T> rightNode = node.right;node.right = null;return rightNode;} else if (node.right == null) {// 2.右子树空,左子树不为空Node<T> leftNode = node.left;node.left = null;return leftNode;} else {// 3.左右子树都不为空// 3-1从右子树中找最小结点Node<T> suffixNode = getMinNode(node.right).get();// 从右子树中删除最小结点suffixNode.right = removeMinNode(node.right);suffixNode.left = node.left;// 将node结点的左右子树失去关联关系node.left = node.right = null;this.size++;return suffixNode;}}// 递归操作if (node.ele.compareTo(ele) > 0) {node.left = remove(node.left, ele);} else {node.right = remove(node.right, ele);}return node;}
删除根结点
public void removeRoot() {if (this.root == null) {return;}remove(this.root.ele);}
相关文章:

数据结构--树
一、树的基本术语 结点:树中的一个独立单元 结点的度:结点下分支的个数 树的度:树中所有结点中度的最大值 非终端结点:度不为0的结点 双亲和孩子:结点下的子树称为该结点的孩子.相应地,该结点称为孩子的双亲 兄弟:同一个双亲的孩子之间 祖先:从根到该结点所经分支上的所…...

计算机网络_1.3电路交换、分组交换和报文交换
1.3电路交换、分组交换和报文交换 一、电路交换1、“电路交换”例子引入2、电路交换的三个阶段3、计算机之间的数据传送不适合采用电路交换 二、分组交换1、发送方(1)报文(2)分组(3)首部 2、交换节点3、接收…...

【AI视野·今日NLP 自然语言处理论文速览 第七十七期】Mon, 15 Jan 2024
AI视野今日CS.NLP 自然语言处理论文速览 Mon, 15 Jan 2024 Totally 57 papers 👉上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Machine Translation Models are Zero-Shot Detectors of Translation Direction Authors Michelle Wastl, Ja…...

神经网络的一些常规概念
epoch:是指所有样本数据在神经网络训练一次(单次epoch(全部训练样本/batchsize)/iteration1)或者(1个epochiteration数 batchsize数) batch-size:顾名思义就是批次大小,也就是一次训练选取的样…...

【从零开始的rust web开发之路 三】orm框架sea-orm入门使用教程
【从零开始的rust web开发之路 三】orm框架sea-orm入门使用教程 文章目录 前言一、引入依赖二、创建数据库连接简单链接连接选项开启日志调试 三、生成实体安装sea-orm-cli创建数据库表使用sea-orm-cli命令生成实体文件代码 四、增删改查实现新增数据主键查找条件查找查找用户名…...

SQL中limit的用法
在SQL中,LIMIT是一个用于限制返回结果行数的关键词。它可用于在查询结果中指定返回的行数,从而可以用于分页查询或限制结果集大小。 LIMIT关键词有两种常用的语法格式: LIMIT offset, count:该语法用于指定返回结果的起始位置和…...

vue3 [Vue warn]: Unhandled error during execution of scheduler flush
文章目录 前言一、报错截图二、排除问题思路相关问题 Vue3 优雅解决方法异步组件异同之处:好处:在使用异步组件时,有几个注意点: vue3 定义与使用异步组件 总结 前言 Bug 记录。开发环境运行正常,构建后时不时触发下面…...

【vue2源码】阶段一:Vue 初始化
文章目录 一、项目目录1、主目录2、打包入口 二、构造函数Vue的初始化1、创建 Vue 构造函数2、初始化内容分析2.1 initMixin2.2 stateMixin2.3 eventsMixin2.4 lifecycleMixin2.5 renderMixin 一、项目目录 源码版本:2.7.16 1、主目录 src |-- compiler # 包…...

14.java集合
文章目录 概念Collection 接口概念示例 Iterator 迭代器基本操作:并发修改异常增强循环遍历数组:遍历集合:遍历字符串:限制 list接口ListIteratorArrayList创建 ArrayList:添加元素:获取元素:修…...

二叉树顺序结构堆实现
目录 Test.c测试代码 test1 test2 test3 🎇Test.c总代码 Heap.h头文件&函数声明 头文件 函数声明 🎇Heap.h总代码 Heap.c函数实现 ☁HeapInit初始化 ☁HeapDestroy销毁 ☁HeapPush插入数据 【1】插入数据 【2】向上调整Adjustup❗ …...

正则表达式 与文本三剑客(sed grep awk)
一,正则表达式 (一)正则表达式相关定义 1,正则表达式含义 REGEXP: Regular Expressions,由一类特殊字符及文本字符所编写的模式,其中有些字符(元字符)不表示字符字面意…...

【XR806开发板试用】全志 XR806 OpenHarmony 鸿蒙系统固件烧录
大家好,我是极智视界,本教程详细记录了全志 XR806 OpenHarmony 鸿蒙系统固件烧录的方法。 在上一篇文章《【嵌入式AI】全志 XR806 OpenHarmony 鸿蒙系统固件编译》中咱们已经编译生成了系统镜像,这里把这个编译出来的镜像烧录到 XR806 板子里…...

linux环境安装git、maven、jenkins等
重启 jenkins的命令: systemctl start jenkins 如果没有vim 命令 可以使用 yum install vim 安装 vim git 下载包地址 https://www.kernel.org/pub/software/scm/git/git-2.28.0.tar.gz 1.安装依赖环境: yum install -y curl-devel expat-devel ge…...

RabbitMQ快速上手
首先他的需求实在什么地方。我美哟明显的感受到。 它给我的最大感受就是脱裤子放屁——多此一举,的感觉。 他将信息发送给服务端中间件。在由MQ服务器发送消息。 服务器会监听消息。 但是它不仅仅局限于削峰填谷和稳定发送信息的功能,它还有其他重要…...

SpringBoot activemq收发消息、配置及原理
SpringBoot集成消息处理框架 Spring framework提供了对JMS和AMQP消息框架的无缝集成,为Spring项目使用消息处理框架提供了极大的便利。 与Spring framework相比,Spring Boot更近了一步,通过auto-configuration机制实现了对jms及amqp主流框架…...

视频智能识别安全帽佩戴系统-工地安全帽佩戴识别算法---豌豆云
视频智能识别安全帽佩戴系统能够从繁杂的工地、煤矿、车间等场景下同时对多个目标是否戴安全帽穿反光衣进行实时识别。 当视频智能识别安全帽佩戴系统发现作业人员没有戴安全帽、穿反光衣或者戴安全带,系统会及时报警提醒,并抓拍存档。 视频智能识别安…...

指针的深入理解(三)
这一节主要使用复习回调函数, 利用冒泡模拟实现qsort函数。 qsort 排序使用冒泡排序,主要难点在于运用元素个数和字节数以及基地址控制元素的比较: if里面使用了一个判断函数,qsort可以排序任意的数据,原因就是因为可…...

【Linux C | 网络编程】详细介绍 “三次握手(建立连接)、四次挥手(终止连接)、TCP状态”
😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C、数据结构、音视频🍭 🤣本文内容🤣&a…...

主从数据库MySQL服务重启步骤与注意事项
主从数据库MySQL服务重启步骤与注意事项 实验环境: 172.20.26.34 (主应用服务器) 172.20.26.26 (备应用服务器) 172.20.26.37 (主库服务器) 172.20.26.38 (从库服务器&…...

netlink学习
netlink是什么 netlink是Linux内核中的一种进程间通信(IPC)机制。它允许内核空间与用户空间之间,以及用户空间进程之间进行双向通信。 内核里的很多子系统使用netlink通信,包括网络管理(Routing,Netfilt…...

地理空间分析10——空间数据分析中的地理编码与Python
目录 写在开头1. 地理编码基础1.1 地理编码的基本原理1.1.1 坐标系统1.1.2 地名解析1.1.3 编码算法1.2 Python中使用地理编码的基础知识1.2.1 百度地图API1.2.2 高德地图API1.2.3 腾讯地图API1.3 Python中实现代码2. 逆地理编码2.1 利用Python进行逆地理编码2.1.1 获取高德地图…...

使用“快速开始”将数据传输到新的 iPhone 或 iPad
使用“快速开始”将数据传输到新的 iPhone 或 iPad 使用 iPhone 或 iPad 自动设置你的新 iOS 设备。 使用“快速开始”的过程会同时占用两台设备,因此请务必选择在几分钟内都不需要使用当前设备的时候进行设置。 确保你当前的设备已连接到无线局域网,并…...

计算机网络(第六版)复习提纲13
前同步码,七位1010交替出现,帧开始码:10101011 为什么没有帧结束?曼彻斯特码传播完成后,维持高电平,不再跳变,因此不必要设置帧结束。 3.无效的MAC帧 i.数据字段的长度与长度字段的值不一致&…...

[office] excel2010双向条形图制作 #经验分享#微信
excel2010双向条形图制作 本教程为大家介绍一下excel2010中excel2010双向条形图制作方法。 1.选中工作区域 2.点击插入-->图表,选择条形图 3.为美观可将中间竖线可去掉 4.方法是选中竖线,右击-->删除 5.接下来将图例靠上,选中图例,右击-->设置图例格式-->图例选项…...

优雅管理多线程异步任务 - 永动异步任务
引言 在现代应用程序中,经常需要处理长时间运行的异步任务,如消息推送、定时任务等。为了确保这些异步任务能够安全可靠地执行,我们需要一种优雅的管理方式。本文将介绍一种基于线程池的多线程异步任务管理方案,并详细讨论任务的优雅关闭。 1. 多线程异步任务管理的需求 …...

软考笔记--分布式数据库
分布式数据库系统是数据库技术与网络技术相结合的产物,其基本思想是将传统的集中式数据库中的数据分布于网络上的多台计算机中。分布式数据库系统通常使用较小的计算机系统,每台计算机可单独放在一个地方,每台计算机中都有DBMS的一份完整的复…...

vue项目中路由懒加载的三种方式
1 . vue异步组件技术 异步加载 vue-router配置路由 , 使用vue的异步组件技术 , 可以实现按需加载 . 但是,这种情况下一个组件生成一个js文件 /* vue异步组件技术 */ { path: /home, name: home, component: resolve > require([/components/home],resolve) }, { path…...

【制作100个unity游戏之23】实现类似七日杀、森林一样的生存游戏6(附项目源码)
本节最终效果演示 文章目录 本节最终效果演示系列目录前言生命 食物 水简单绘制UI玩家状态脚本生命值控制饱食度控制水分控制 源码完结 系列目录 前言 欢迎来到【制作100个Unity游戏】系列!本系列将引导您一步步学习如何使用Unity开发各种类型的游戏。在这第23篇中…...

Cache Lab:Part A【模拟出使用LRU策略的高速缓存存储器组织结构】
目录 任务描述 知识回顾 实验内容 测试结果 Cache Lab 对应《CS:APP》6.3节至第六章结束的内容。 任务描述 Your job for Part A is to fill in the csim.c file so that it takes the same command line arguments and produces the identical output as the reference …...

操作系统基础:死锁
🌈个人主页:godspeed_lucip 🔥 系列专栏:OS从基础到进阶 🐦1 死锁的概念🦢1.1 总览🦢1.2 什么是死锁🦢1.3 死锁、饥饿、死循环的区别🐧1.3.1 概念🐧1.3.2 区别…...