十三、二叉排序树

news2024/10/2 3:32:43

1、先看一个需求

给你一个数列 {7, 3, 10, 12, 5, 1, 9} ,要求能够高效的完成对数据的查询和添加

2、解决方案分析

  • 使用数组
    数组未排序,优点:直接在数组尾添加,速度快。缺点:查找速度慢
    数组排序,优点:可以使用二分查找,查找速度快。缺点:为了保证数组有序,在添加新数据时,找到插入位置后,后面的数据需要整体移动,速度慢
  • 使用链表
    不管链表是否有序,查找速度都慢,添加数据速度比数组快,不需要数据整体移动
  • 使用二叉排序树

3、二叉排序树介绍

二叉排序树:BST:(Binary Sort Tree),对于二叉排序树的任何一个非叶子节点,要求左子节点的值比当前节点的值小,右子节点的值比当前节点的值大。
特别说明:如果有相同的值,可以将该节点放在左子节点或右子节点

比如针对前面的数据 {7, 3, 10, 12, 5, 1, 9},对应的二叉排序树为:
在这里插入图片描述

4、二叉排序树的创建和遍历

一个数组创建成对应的二叉排序树,并使用中序遍历二叉排序树

5、二叉排序树的删除

有三种情况需要考虑:

  • 删除叶子节点(比如:2,5,9,12)
  • 删除只有一棵子树的节点(比如:1)
  • 删除有两棵子树的节点(比如:7,3,10)

思路分析
情况一:
(1)需要先找到要删除的节点targetNode
(2)找到tartgetNode的父节点parent
(3)确定targetNode是parent的左子节点还是右子节点
(4)根据前面的情况来对应删除
左子节点 parent.left = null
右子节点parent.right = null;

情况二:
(1)需要先找到要删除的节点targetNode
(2)找到tartgetNode的父节点parent
(3)确定targetNode的子节点是左子节点还是右子节点
(4)targetNode是是parent的左子节点还是右子节点
(5)如果targetNode有左子节点
5.1 如果targetNode是parent的左子节点
parent.left = targetNode.left
5.2 如果targetNode是parent的右子节点
parent.right = targetNode.left
(6)如果targetNode有右子节点
6.1 如果targetNode是parent的左子节点
parent.left = targetNode.right
6.2 如果targetNode是parent的右子节点
parent.right = targetNode.right

情况三:
(1)需要先找到要删除的节点targetNode
(2)找到tartgetNode的父节点parent
(3)从targetNode的右子树找到最小的节点
(4)用一个临时变量,将最小节点的值保存temp
(5)删除该最小节点
(6)targetNode.value = temp

6、代码实现

public class BinarySortTreeDemo {

	public static void main(String[] args) {
		int[] arr = new int[] { 7, 3, 10, 12, 5, 1, 9, 2 };
		BinarySortTree binarySortTree = new BinarySortTree();
		for (int i = 0; i < arr.length; i++) {
			binarySortTree.add(new Node(arr[i]));
		}
		// 中序遍历二叉排序树
//		System.out.println("中序遍历二叉排序树");
//		binarySortTree.infixOrder();// 1 3 5 7 9 10 12

//		binarySortTree.add(new Node(11));

		// 测试删除叶子节点
//      binarySortTree.delNode(2);
//      binarySortTree.delNode(10);
		binarySortTree.delNode(1);
		System.out.println("测试删除节点后");
		binarySortTree.infixOrder();

	}

}

class BinarySortTree {

	private Node root;

	// 查找要删除的节点
	public Node search(int value) {
		if (root == null) {
			return null;
		} else {
			return root.search(value);
		}
	}

	// 查找父节点
	public Node searchParent(int value) {
		if (root == null) {
			return null;
		} else {
			return root.searchParent(value);
		}
	}

	/**
	 * 1.返回的是以node为根节点的二叉排序树的最小节点的值 2.删除以node为根节点的二叉排序树的最小节点的
	 * 
	 * @param node 这个表示传入的节点(当作二叉排序树的根节点)
	 * @return 返回的是以node为根节点的二叉排序树的最小节点的值
	 */
	public int delRightTreeMin(Node node) {
		Node target = node;
		while (target.left != null) {
			target = target.left;
		}
		delNode(target.value);
		return target.value;
	}

	public void delNode(int value) {
		// 1.需要先找到要删除的节点 targetNode
		Node targetNode = search(value);
		// 如果没有找到要删除的节点
		if (targetNode == null) {
			return;
		}
		// 如果我们发现当这个二叉排序树只有一个节点,而且这个节点就是要查找删除的节点,直接置空删除即可
		if (root.left == null && root.right == null) {
			root = null;
			return;
		}
		// 2.去找targetNode的父节点
		Node parent = searchParent(value);
		// 情况一:如果删除的节点是叶子节点
		if (targetNode.left == null && targetNode.right == null) {
			// 判断targetNode是父节点的左子节点还是右子节点
			if (parent.left != null && parent.left.value == value) {// 是左子节点
				// 说明要删除的targetNode就是要删除的
				parent.left = null;
			} else if (parent.right != null && parent.right.value == value) {// 是右子节点
				parent.right = null;
			}
		} else if (targetNode.left != null && targetNode.right != null) {// 情况三:删除的节点有两个子树
			int minValue = delRightTreeMin(targetNode.right);
			targetNode.value = minValue;
		} else {
			// 情况二,删除只有一棵子树的节点,因为前两种情况都排除了
			// 如果要删除的节点有左子节点
			if (targetNode.left != null) {
				if (parent != null) {
					// 如果targetNode是parent的左子节点
					if (parent.left.value == value) {
						parent.left = targetNode.left;
					} else {
						parent.right = targetNode.left;
					}
				} else {
					root = targetNode.left;
				}
			} else {// 表示要删除的节点有右节点
				if (parent != null) {
					// 如果targetNode是parent的左子节点
					if (parent.left.value == value) {
						parent.left = targetNode.right;
					} else {
						parent.right = targetNode.right;
					}
				} else {
					root = targetNode.right;
				}
			}
		}

	}

	public void add(Node node) {
		if (root == null) {
			root = node;
		} else {
			root.add(node);
		}
	}

	public void infixOrder() {
		if (root == null) {
			System.out.println("当前这棵二叉树为空,不能遍历");
		} else {
			root.infixOrder();
		}
	}

}

class Node {
	int value;
	Node left;
	Node right;

	public Node(int value) {
		this.value = value;
	}

	@Override
	public String toString() {
		return "Node [value=" + value + "]";
	}

	/**
	 * 查找要删除的节点
	 * 
	 * @param value 待删除的节点的值
	 * @return 如果找到,返回该节点,否则返回null
	 */
	public Node search(int value) {
		if (value == this.value) {
			return this;
		} else if (value < this.value) {
			// 如果查找的值小于当前节点,向左递归查找
			// 如果左子节点为空
			if (this.left == null) {
				return null;
			}
			// 左子节点不为空,才可以左递归
			return this.left.search(value);
		} else {
			// 如果要查找的值不小于当前节点的值,那么右递归查找
			if (this.right == null) {
				return null;
			}

			return this.right.search(value);

		}
	}

	/**
	 * 查找要删除节点的父节点
	 * 
	 * @param value 待删除的节点的值
	 * @return 待删除节点的父节点,如果没有,则返回null
	 */
	public Node searchParent(int value) {
		// 如果当前节点就是要删除节点的父节点
		if ((this.left != null && this.left.value == value) || (this.right != null && this.right.value == value)) {
			// 此时this就是当前节点的父节点
			return this;
		} else {
			// 如果查找的值小于当前节点的值,并且当前节点的左子节点不为空
			if (value < this.value && this.left != null) {
				return this.left.searchParent(value);
			} else if (value > this.value && this.right != null) {
				return this.right.searchParent(value);
			} else {
				return null;
			}
		}
	}

	// 添加节点的方法,递归的形式进行添加节点,需要满足二叉排序树
	public void add(Node node) {
		if (node == null) {
			System.out.println("数据为空,不能判断");
			return;
		}
		// 判断传入节点的值,和当前子树根节点的关系
		if (node.value < this.value) {
			// 如果当前节点左子节点为null,直接放到左子节点即可
			if (this.left == null) {
				this.left = node;
			} else {
				// 如果左子节点不为空,直接向左边递归即可
				this.left.add(node);
			}

		} else {
			// 如果传入node.value大于等于当前节点的值,那就需要再进行判断
			// 先看右子节点是否为空
			if (this.right == null) {
				this.right = node;
			} else {
				// 递归的向右子树添加
				this.right.add(node);
			}
		}
	}

	// 中序遍历
	public void infixOrder() {
		if (this.left != null) {
			this.left.infixOrder();
		}
		System.out.println(this);
		if (this.right != null) {
			this.right.infixOrder();
		}

	}
}

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

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

相关文章

如何推出 NFT 游戏并获得收益?- 综合指南

NFT Gaming - 简介 - NFT 是代表独特内容所有权的数字资产&#xff0c;例如游戏中的一件艺术品或收藏品。它们建立在区块链解决方案之上&#xff0c;允许这些数字资产的真正所有权和稀缺性。 在游戏行业&#xff0c;NFT 用于创建玩家可以拥有和交易的独一无二的游戏内物品。这允…

政企服务机构如何进行数字化转型?

对于服务于政府和企业的产业机构来说&#xff0c;将政府政策和企业发展做完美匹配结合是其根本。在这个过程中&#xff0c;政策信息的准确性、前瞻性、核心价值是服务的基础&#xff0c;只有在政策下可行性高的解决方案才能为政府吸引更多的企业入驻。 那么该类产业机构该如何…

如何理解​session、cookie、token的区别与联系?

session、cookie、token。 相信学过接口的朋友都特别熟悉了。 但是对我一个刚接触接口测试的小白来说&#xff0c;属实有点分不清楚。 下文就是我通过查阅各种资料总结出来的一点理解&#xff0c;不准确的地方还请各位指正。 &#xff08;文末送洗浴中心流程指南&#xff09…

C语言(函数和递归)

函数是完成特定任务的独立程序代码单元。 目录 一.函数 1.创建一个简单的函数 2.定义带形式参数的函数 3.使用return从函数中返回值 二.递归 一.函数 1.创建一个简单的函数 #include <stdio.h> void print(void); //函数原型 int main(){ print(); //函…

Weblogic CVE之旅之T3协议浅学习

前言 这篇文章主要是学习Weblogic CVE漏洞中的过程中&#xff0c;对其中的一种利用方式T3协议反序列化漏洞进行分析。 前置 什么是T3协议&#xff1f; T3协议是一种Weblogic RMI 调用时的通信协议, 对于JAVA RMI(Remote Method Invocation) 来说&#xff0c;基础的通信协议…

Web前端:使用Angular CLI时的最佳实践和专业技巧

在web开发业务中&#xff0c;构建高性能的应用程序是首要因素。此外&#xff0c;用开发人员最流行的语言开发一个健壮的网站将始终为构建高功能的网站提供适当的基础网站。相比之下&#xff0c;不可否认&#xff0c;Angular CLI是建立得最好且正在成长的框架之一。Angular CLI简…

【蓝桥杯嵌入式】第十三届蓝桥杯嵌入式 国赛 程序设计试题以及详细题解

文章目录原题展示原题分析详细题解LED模块按键模块串口LCD模块模拟电压读取(ADC)脉冲输入输出文章福利原题展示 原题分析 本届国赛试题主要包含LCD、LED、按键、EEPROM、串口、模拟电压输入、脉冲输入输出七大部分&#xff0c;其中前面三个部分是蓝桥杯嵌入式的“亲儿子”(必考…

2023年妇女节是哪一天 妇女节是2023年几月几日?

2023年妇女节是哪一天是2023年几月几日&#xff1f; 2023年妇女节是2023年3月8日 三八妇女节是国家法定节假日吗&#xff1f; 妇女节不是国家法定节假日&#xff0c;而国家法定节假日包括&#xff1a;元旦、春节、清明节、劳动节、端午节、中秋节、国庆节&#xff1b; 关于三…

操作系统(day09) -- 连续分配管理方式

连续分配管理方式 单元连续分配 动态分区分配 1.系统要用什么样的数据结构记录内存的使用情况&#xff1f; 两种常用的数据结构 空闲分区表 每个空闲分区对应一个表项。表项中包含分区号、分区大小、分区起始地址等信息空闲分区链 每个分区的起始部分和末尾部分分别设置前向…

硬件学习 软件Cadence day02 画原理图的基本操作 (键盘快捷键 , 原理图设计流程 , 从开始到导出网表流程)

1. ORCAD Capture cls 界面的快捷键 键盘 按键对应的操作I放大 &#xff08;可以滚轮操作&#xff09;O缩小 &#xff08;可以滚轮操作&#xff09;W画线Esc退出现在的状态 &#xff08;画图界面 右键 End xxx&#xff09;N放置网络标号J放置节点 (控制…

花2个月面过华为测开岗,拿个30K不过分吧?

背景介绍 美本计算机专业&#xff0c;代码能力一般&#xff0c;之前有过两段实习以及一个学校项目经历。第一份实习是大二暑期在深圳的一家互联网公司做前端开发&#xff0c;第二份实习由于大三暑假回国的时间比较短&#xff08;小于两个月&#xff09;&#xff0c;于是找的实…

ChatGPT is not all you need,一文看尽SOTA生成式AI模型:6大公司9大类别21个模型全回顾(一)

文章目录ChatGPT is not all you need&#xff0c;一文看尽SOTA生成式AI模型&#xff1a;6大公司9大类别21个模型全回顾&#xff08;一&#xff09;Text-to-Image 模型DALL-E 2IMAGENStable DiffusionMuseText-to-3D 模型DreamfusionMagic3DChatGPT is not all you need&#x…

Python和Java语言,哪个更适合做自动化测试?

经常有测试新手问我&#xff1a;Python和Java语言&#xff0c;哪个更适合做自动化测试&#xff1f;本来想简单的回答一下的&#xff0c;但又觉得对不起大家对小编的信任。因此&#xff0c;小编今天专门写了一篇文章来回答这个问题。欢迎各位大佬补充~1、什么是自动化测试&#…

大数据框架之Hadoop:HDFS(八)HDFS HA高可用

8.1 HA概述 1&#xff09;所谓HA&#xff08;High Available&#xff09;&#xff0c;即高可用&#xff08;7*24小时不中断服务&#xff09;。 2&#xff09;实现高可用最关键的策略是消除单点故障。HA严格来说应该分成各个组件的HA机制&#xff1a;HDFS的HA和YARN的HA。 3&…

一文优化java.lang.StackOverflowError的堆栈溢出问题及递归引发的java.lang.StackOverflowError错误

文章目录1. 问题引出2. 分析问题2.1 为什么递归调用会导致堆栈溢出2.2 数组太大或分配内存多于可用内存导致堆栈异常3. 优化避免栈溢出3.1 尾递归优化3.2 循环替代递归4. 重要总结1. 问题引出 今天在编码时&#xff0c;出现了java.lang.StackOverflowError&#xff0c;就感觉很…

【Python小游戏】没点儿技术真不敢这么玩儿:人工智能挑战《贪吃蛇》,来自AI的艺术——超级游戏高手世界最高纪录秒被盘?

前言 每天分享各种Python项目、好玩的Pygame游戏、Python的爬虫、数据分析案例、有趣的人 工智能知识等。期待你的关注哦&#xff01; 所有文章完整的素材源码都在&#x1f447;&#x1f447; 粉丝白嫖源码福利&#xff0c;请移步至CSDN社区或文末公众hao即可免费。 哈喽&…

探索IP地址的应用

无论是互联网行业还是传统行业都会用到网络&#xff0c;作为企业如何维护网络安全&#xff0c;保障网站不被攻击&#xff0c;数据不被泄露等。这个时候我们就会通查询IP归属地&#xff0c;辅助企业解决安全问题。下面介绍一下ip归属地在各行业的具体应用。1.网安行业应用一&…

SpringBoot实现登录拦截器超详细(springboot拦截器excludePathPatterns方法不生效的坑)

文章目录SpringBoot实现登录拦截器1、SpringBoot 实现登录拦截的原理1.1、实现HandlerInterceptor接口1.2、实现WebMvcConfigurer接口&#xff0c;注册拦截器1.3、保持登录状态springboot拦截器excludePathPatterns方法不生效的坑与解决方法一、前言二、问题三、解决方法四、总…

C语言编译过程

C语言编译过程1、C语言编译过程2、单c文件编译实践3、多c文件编译实践4、define4.1、不带参宏4.2、带参宏4.3、带参宏和带参函数的区别5、选择性编译ifdef、ifndef、if5.1、#ifdef5.2、#ifndef5.3、#if6、静态库和动态链接库6.1、静态库实践6.1.1、将mylib.c制作成静态库6.1.2、…

Baklib知识库管理平台,协助组织提升知识管理水平

随着信息时代和知识经济时代的到来&#xff0c;企业内部信息资料繁多冗杂&#xff0c;知识管理逐渐成为各大企业的重要工作之一&#xff0c;企业管理者无不感受到巨大的压力&#xff0c;怎么样将知识进行有效的管理&#xff0c;成为一个难点&#xff0c;并且随着信息不断的更迭…