数据结构-二分搜索树(Binary Search Tree)
一,简单了解二分搜索树
树结构:

问题:为什么要创造这种数据结构
1,树结构本身是一种天然的组织结构,就好像我们的文件夹一样,一层一层的.

2,树结构可以更高效的处理问题
二,二分搜索树的基础
1、二叉树


2,二叉树的重要特性
满二叉树

总结:
1. 叶子结点出现在二叉树的最底层,除叶子结点之外的其它结点都有两个孩子结点。
2. 一个层数为k 的满二叉树总结点数为:
3. 第i层上的结点数为:
4. 一个层数为k的满二叉树的叶子结点个数(也就是最后一层):
4、二叉树不一定是“满”的

3,二分搜索树


(注意:存储的元素必须具有可比性)
1,向二分搜索树添加元素

2,向二分搜索树查询操作
1,递归终止的条件 : if(node == null ) return false;
2,递归操作
if (ele.compareTo(node.ele) < 0) {
return search(node.left, ele);
} else if (ele.compareTo(node.ele) > 0) {
return search(node.right, ele);
} else {
return true;
}
3,二分搜索树的遍历操作
遍历操作:把树中所有节点都访问一遍
1前序遍历,
2中序遍历,
3后序遍历
4层序遍历(广度优先)
(代码,会在后面一起展现.)
4,二分搜索树寻找最大值,最小值
同样的原理找出二分搜素树中最大的元素,这里不在过多赘述.
5,删除操作
情况一:(叶子结点)

情况二、(非叶子结点)

删除后
6,删除二分搜索树中的节点
情况一:

情况二、

情况三:左右孩子都不为空的情况
使用Hibbard Deletion

三,用代码实现二分搜索树.实现相关功能.
(由于功能实现较多,代码较长)
其中包含是前面的各种功能操作的实现,包括,前,中,后,层,序把遍历,删除,添加,等等操作.需要的同学可以仔细查看.
mport java.nio.channels.Pipe;
import java.util.*;
import java.util.stream.Collectors;// 二分搜索树
public class BinarySearchTree<T extends Comparable<T>> {// 定义树的结点public class Node {T val;Node left; // 左孩子Node right; // 右孩子public Node(T val) {this.val = val;}}// 定义树的根private Node root;// 树根// 统计树中结点的个数private int size;// 树中结点的个数public BinarySearchTree() {this.root = null;this.size = 0;}// 判断树是否为空public boolean isEmpty() {return this.size == 0;}// 获取树中元素的个数public int getSize() {return this.size;}// 向树中添加元素public void add(T val) {this.size++;this.root = add(this.root, val);}/*** 添加的递归方法** @param node 树的根结点* @param val 添加的值*/private Node add(Node node, T val) {//递归终止的条件if (node == null) {Node leafNode = new Node(val);return leafNode;}// 递归操作if (node.val.compareTo(val) > 0) {node.left = add(node.left, val);} else {node.right = add(node.right, val);}return node;}// 将树中所有结点进行遍历--中序遍历( 深度优先搜索 DFS,使用的栈来实现)public String middleTravel() {List<T> result = new ArrayList<>();middleTravel(this.root, result);return result.stream().map(item -> item.toString()).collect(Collectors.joining(","));}/*** 中序遍历** @param node 树的根结点*/private void middleTravel(Node node, List<T> result) {// 递归终止的条件if (node == null) {return;}// 递归操作// 先遍历左子树middleTravel(node.left, result);// 打印当前值result.add(node.val);// 再遍历右子树middleTravel(node.right, result);}public String beforeTravel() {List<T> result = new ArrayList<>();beforeTravel(this.root, result);return result.stream().map(item -> item.toString()).collect(Collectors.joining(","));}/*** 前序遍历** @param node 树的根结点*/private void beforeTravel(Node node, List<T> result) {// 递归终止的条件if (node == null) {return;}// 递归操作// 打印当前值result.add(node.val);// 先遍历左子树beforeTravel(node.left, result);// 再遍历右子树beforeTravel(node.right, result);}public String afterTravel() {List<T> result = new ArrayList<>();afterTravel(this.root, result);return result.stream().map(item -> item.toString()).collect(Collectors.joining(","));}/*** 后序遍历** @param node 树的根结点*/private void afterTravel(Node node, List<T> result) {// 递归终止的条件if (node == null) {return;}// 递归操作// 先遍历左子树afterTravel(node.left, result);// 再遍历右子树afterTravel(node.right, result);// 打印当前值result.add(node.val);}// 查找的方法public boolean contains(T val) {return contains(this.root, val);}/*** 从以node为根的二分搜索树中查找元素val** @param node 根节点* @param val 要搜索的值* @return*/private boolean contains(Node node, T val) {// 递归到底的情况if (node == null) {return false;}// 递归操作if (node.val.compareTo(val) == 0) {return true;} else if (node.val.compareTo(val) > 0) {return contains(node.left, val);} else {return contains(node.right, val);}}// 树的层序遍历(广度优先搜索 BFS,使用队列来实现)public String levelTravel() {List<String> list = new ArrayList<>();// 1、 创建一个队列Queue<AbstractMap.SimpleEntry<Integer, Node>> queue = new LinkedList<>();// 2、将根结点入入队if (this.root != null) {queue.offer(new AbstractMap.SimpleEntry<>(1, this.root));}// 3、遍历队列while (!queue.isEmpty()) {AbstractMap.SimpleEntry<Integer, Node> temp = queue.poll();Node value = temp.getValue();int key = temp.getKey();//3-1 先将内容保存list.add(value.val.toString() + "------" + key);// 3-2 判断左子树是否为空,不为空就入队if (value.left != null) {queue.offer(new AbstractMap.SimpleEntry<>(key + 1, value.left));}// 3-3 判断右子树是否为空,不为空就入队if (value.right != null) {queue.offer(new AbstractMap.SimpleEntry<>(key + 1, value.right));}}return list.stream().collect(Collectors.joining(","));}public List<List<T>> levelOrder() {// 返回值类型是啥,就创建啥List<List<T>> result = new ArrayList<>();// 1、 创建一个队列Queue<AbstractMap.SimpleEntry<Integer, Node>> queue = new LinkedList<>();// 2、将根结点入入队if (this.root != null) {queue.offer(new AbstractMap.SimpleEntry<>(1, this.root));}while (!queue.isEmpty()) {AbstractMap.SimpleEntry<Integer, Node> temp = queue.poll();Node value = temp.getValue();int key = temp.getKey();//3-1 先将内容保存if(result.get(key-1)==null){result.add(new ArrayList<>());}result.get(key-1).add(value.val);// 3-2 判断左子树是否为空,不为空就入队if (value.left != null) {queue.offer(new AbstractMap.SimpleEntry<>(key + 1, value.left));}// 3-3 判断右子树是否为空,不为空就入队if (value.right != null) {queue.offer(new AbstractMap.SimpleEntry<>(key + 1, value.right));}}return result;}// Pair对public class Pair<Node> {private Node value; // 保存值private int key; // 保存层public Pair(Node value, int key) {this.value = value;this.key = key;}public Node getValue() {return value;}public int getKey() {return key;}}// 从二分搜索树中找最小值(在整棵树的最左边)public T getMinVal() {// 判断树是否为空if (this.root == null) {return null;}Node curNode = this.root;while (curNode.left != null) {curNode = curNode.left;}return curNode.val;}public T getMinValDG() {// 判断树是否为空if (this.root == null) {return null;}return getMinValDG(this.root).val;}/*** 从以node为根的二分搜索树中查找最小值** @param node 树的根节点*/private Node getMinValDG(Node node) {//递归终止的条件if (node.left == null) {return node;}// 递归操作return getMinValDG(node.left);}// 从二分搜索树中找最 大值(在整棵树的最右边)public T getMaxVal() {// 判断树是否为空if (this.root == null) {return null;}Node curNode = this.root;while (curNode.right != null) {curNode = curNode.right;}return curNode.val;}public T getMaxValDG() {// 判断树是否为空if (this.root == null) {return null;}return getMaxValDG(this.root).val;}private Node getMaxValDG(Node node) {//递归终止的条件if (node.right == null) {return node;}// 递归操作return getMinValDG(node.right);}// 从以this.root为根的二分搜索树中删除最小的结点public void removeMinNode() {if (this.root == null) {return;}this.root = removeMinNode(this.root);}/*** 从以node为根的二分搜索树中删除最小的节点** @param node 树的根节点* @return 删除之后的树的根节点*/private Node removeMinNode(Node node) {// 递归终止的条件if (node.left == null) {this.size--;return node.right;}// 递归操作node.left = removeMinNode(node.left);return node;}// 删除操作public void remove(T val) {if (!contains(val)) {return;}this.root = remove(this.root, val);}/*** 从以node为根的二分搜索树中删除元素val的节点** @param node 树的根节点* @param val 删除的值* @return*/private Node remove(Node node, T val) {// 递归终止的条件if (node == null) {return null;}if (node.val.compareTo(val) == 0) {// 更新sizethis.size--;if (node.right == null) {//1、右子树为空return node.left;} else if (node.left == null) {//2、左子树为空return node.right;} else {// 3、左右子树都不为空// 3-1 找到删除节点的后继Node suffixNode = getMinValDG(node.right);// 3-2 更新suffixNode的左右子树
// suffixNode.right = removeMinNode(node.right);suffixNode.right = remove(node.right, getMinValDG(node.right).val);suffixNode.left = node.left;this.size++;// 3-3 返回suffixNodereturn suffixNode;}}// 递归操作if (node.val.compareTo(val) > 0) {node.left = remove(node.left, val);} else {node.right = remove(node.right, val);}return node;}}
相关文章:
数据结构-二分搜索树(Binary Search Tree)
一,简单了解二分搜索树 树结构: 问题:为什么要创造这种数据结构 1,树结构本身是一种天然的组织结构,就好像我们的文件夹一样,一层一层的. 2,树结构可以更高效的处理问题 二,二分搜索树的基础 1、二叉树 2,二叉树的重要特性 满二叉树 总结: 1. 叶子结点出现在二叉树的最…...
YOLO如何训练自己的模型
目录 步骤 一、打标签 二、数据集 三、跑train代码出模型 四、跑detect代码出结果 五、详细操作 步骤 一、打标签 (1)在终端 pip install labelimg (2)在终端输入labelimg打开 如何打标签: 推荐文章…...
05 EXTI外部中断
一、中断系统 中断系统:管理和执行中断的逻辑结构。中断:在主程序运行过程中,出现了特定的中断触发条件——中断源,使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继…...
2024.2.23
1.1.1 信号默认、捕获、忽略处理(普通信号) #include <myhead.h> void handler(int signo) {if(signoSIGINT){printf("用户键入 ctrlc\n");} } int main(int argc, const char *argv[]) {//忽略信号if(signal(SIGINT,SIG_IGN)SIG_ERR){perror("signal er…...
PHP实现分离金额和其他内容便于统计计算
得到的结果可以粘贴到excel计算 <?php if($_GET["x"] "cha"){ $tips isset($_POST[tips]) ? $_POST[tips] : ; $pattern /(\d\.\d|\d)/; $result preg_replace($pattern, "\t\${1}\t", $tips); echo "<h2><strong>数…...
基础数据结构和算法《》
递归 1.递归应该一种比较常见的实现一些特殊代码逻辑时需要做的,但常常也是最绕的一种方式,在解释递归 之前,我们用循环和递归来做个比较1.1.如果你打开一扇门后,同样发现前方也有一扇们,紧接着你又打开下一扇门...直…...
[设计模式Java实现附plantuml源码~行为型]对象间的联动~观察者模式
前言: 为什么之前写过Golang 版的设计模式,还在重新写Java 版? 答:因为对于我而言,当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言,更适合用于学习设计模式。 为什么类图要附上uml 因为很…...
vue3+js 实现记住密码功能
常见的几种实现方式 1 基于spring security 的remember me 功能 localStorage 除非主动清除localStorage 里的信息 ,不然永远存在,关闭浏览器之后下次启动仍然存在 存放数据大小一般为5M 不与服务器进行交互通信 cookies 可以…...
基于单片机的太阳能电池板自动跟踪系统的研究
摘要 伴随着人类社会的发展,人口基数越来越大,电量消耗巨大,传统发电原 料污染环境的同时,可用量日益减少,给人类未来生产生活带来了一定的威胁, 因而解决日益剧增的用电量,寻求一种新能源显得极其重要。论文正是基于此 背景下,针对当前太阳能电池板采光率低、自动化水…...
React 模态框的设计(二)
自定义组件是每个前端开发者必备的技能。我们在使用现有框架时难免有一些超乎框架以处的特别的需求,比如关于弹窗,每个应用都会用到,但是有时我们使用的框架中提供的弹窗功能也是功能有限,无法满足我们的应用需求,今天…...
操作符详解3
✨✨ 欢迎大家来到莉莉的博文✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 前面我们已经讲过算术操作符、赋值操作符、逻辑操作符、条件操作符和部分的单目操作 符,今天继续介绍一部分。 目录 1.操作符的分类 2…...
【C语言基础】:操作符详解(一)
文章目录 操作符详解1. 操作符的分类2. 二进制和进制转换2.1 什么是二进制、八进制、十进制、十六进制2.1.1 二进制和进制转换2.1.2 二进制转十进制2.2.3 二进制转八进制2.2.4 二进制转十六进制 3. 源码、反码、补码4. 移位操作符4.1 左移操作符4.2 右移操作符 5. 位操作符&…...
通俗易懂分析:Vite和Webpack的区别
1、对项目构建的理解 先从浏览器出发, 浏览器是由浏览器内核和JS引擎组成;浏览器内核编译解析html代码和css代码,js引擎编译解析JavaScript代码;所以从本质上,浏览器只能识别运行JavaScript、CSS、HTML代码。 而我们在…...
OpenCart程序结构与业务逻辑
一、程序业务逻辑说明 在 OpenCart 中,index.php 文件是整个应用程序的入口文件,它负责初始化应用程序并调度请求。以下是 index.php 文件加载执行的流程: 1. **设置路径常量:** - index.php 首先定义了一些重要的路径常量&…...
软件License授权原理
软件License授权原理 你知道License是如何防止别人破解的吗?本文将介绍License的生成原理,理解了License的授权原理你不但可以防止别人破解你的License,你甚至可以研究别人的License找到它们的漏洞。喜欢本文的朋友建议收藏+关注,方便以后复习查阅。 什么是License? 在…...
C/C++实现老鼠走迷宫
老鼠形象可以辨认,可以用上下左右操纵老鼠;正确检测结果,若老鼠在规定的时间内走到粮仓,提示成功,否则提示失败。代码分为3个文件:main.cpp、play.h、play.cpp。 main.cpp: #include <iostream> #include <…...
[Linux]文件基础-如何管理文件
回顾C语言之 - 文件如何被写入 fopen fwrite fread fclose fseek … 这一系列函数都是C语言中对文件进行的操作: int main() {FILE* fpfopen("text","w");char str[20]"write into text";fputs(str,fp);fclose(fp);return 0; }而上…...
bat 查找文件所在
脚本 在批处理文件(.bat)中查找文件所在的目录,你可以使用dir命令结合循环和条件语句来实现。以下是一个简单的示例,演示如何在批处理文件中查找指定文件并输出其所在目录: echo off setlocal enabledelayedexpansio…...
程序员的守护神:为何电脑永不熄灭?
在这个信息时代,程序员成了推动社会进步的“隐形英雄”。他们通宵达旦,手指在键盘上跳跃,创造出一个个令人惊叹的数字世界。有趣的是,你可能注意到了一个现象:程序员似乎总是不关电脑。这并非他们对电脑上瘾࿰…...
Kafka快速实战以及基本原理详解
Kafka快速实战以及基本原理详解 基本概念 Kafka是一个分布式、支持分区、多副本,基于ZK的分布式消息系统,最大的特性就是可以实时的处理大量数据以满足各种需求场景,比如Hadoop的批处理系统、低延迟的实时系统、Storm/Spark流式处理引擎、日…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...

