二叉查找树 Java实现
定义:
一棵二叉查找树是一棵二叉树,每个节点都含有一个Comparable的键(以及对应的值)。
每个节点的键都大于左子树中任意节点的键而小于右子树中任意节点的键。
树的术语:
Name | Function |
---|---|
路径 | 顺着连接点的边从一个节点走向另一个节点,所经过的节点的顺序排列就称为路径。 |
根 | 树顶端的节点就称为根,一棵树只有一个根,如果要把一个节点和边的集合定义为树,那么从根到其他任何一个节点都必须有一条路径。 |
父节点 | 每个节点(除了根)都恰好有一条边向上连接到另一个节点,上面的节点就称为下面节点的“父节点”。 |
子节点 | 每个节点都可能有一条或多条边向下连接其他节点,下面的这些节点就称为它的“子节点”。 |
叶节点 | 没有子节点的节点称为“叶子节点”或简称“叶节点”。树只能有一个根,但是可以有很多叶节点。 |
子树 | 每个节点都可以作为子树的根,它和它所有的子节点,子节点的子节点等都含在子树中。 |
访问 | 当程序控制流程到达某个节点的时候,就称为“访问”这个节点,通常是为了在这个节点处执行某种操作,例如查看节点某个数据字段的值或者显示节点。 |
遍历 | 遍历树意味着要遵循某种特定的顺序访问树中的所有节点。 |
层 | 一个节点的层数是指从根开始到这个节点有多少“代”。 |
关键字 | 可以看到,对象中通常会有一个数据域被指定为关键字值。这个值通常用于查询或者其他操作。 |
二叉树 | 如果树中的每个节点最多只能有两个子节点,这样的树就称为“二叉树”。 |
性质:
- 若它的左子树不为空,则左子树上的所有节点的值都小于它的根节点的值;
- 若它的右子树不为空,则右子树上所有节点的值都大于它的根节点的值;
- 其他的左右子树也分别为二叉查找树;
- 二叉查找树是动态查找表,在查找的过程中可见添加和删除相应的元素,在这些操作中需要保持二叉查找树的以上性质。
根据其二叉树的特性,节点类如下:
public class Node {
public int index;//关键字段
public String data;//值
public Node leftNode;//左节点
public Node rightNode;//右节点
@Override
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}
其中引用了commons-lang3包中的内容,作为对象进行比较
查找某个节点,相当于二分查找,如果小于当前节点,则走左边,如果大于当前节点,则走右边。当最后叶子节点还没有找到,则没有找到
public Node findNode(int key){
Node current = root;
while(current.index != key){
if(key < current.index){//左节点
current = current.leftNode;
}else{//右节点
current = current.rightNode;
}
if(current == null){
return null;
}
}
return current;
}
插入节点,按照插入的节点都不会出现重复关键字。每一次插入都将变为根节点的子节点,例如根节点关键字为1,接下来插入的节点则变为根节点的右子节点。
public void insertNode(int key,String value){
Node node = new Node();
node.index = key;
node.data = value;
if(root == null){
root = node;
return;
}
//找到插入节点的位置
Node parent = root;
Node current = root;
while(true){
parent = current;
if(key == current.index){
return;
}
if(key < current.index){//左节点
current = current.leftNode;
if(current == null){//当前节点已经是叶子结点了
parent.leftNode = node;
return;
}
}else{
current = current.rightNode;
if(current == null){
parent.rightNode = node;
return;
}
}
}
}
遍历节点,中序遍历.
- 调用自身来遍历节点的左子树
- 访问这个节点
- 掉用自身来遍历节点的右子树
public void inOrder(Node localRoot) {
if (localRoot != null) {
inOrder(localRoot.leftNode);
System.out.println("索引:" + localRoot.index + ",值:" + localRoot.data);
inOrder(localRoot.rightNode);
}
}
删除节点,分三种情况:
- 删除节点没有子节点,那么将父节点的左节点或者是右节点设置为空
- 删除节点只有一个子节点,删除该节点后,该节点的子节点变为父节点的子节点,如果删除节点时父节点的左节点,那么父节点的左节点指向该节点的子节点,反之则右节点指向删除节点的子节点。
- 删除节点有两个字节点,删除了该节点后,则需要选择一个后继节点,并且还不破坏该二叉树的特性(左节点要小于右节点),一般是从删除节点的右节点中找到一个后继节点,而这个节点是右子树的最小值。
public boolean delete(int key) {
Node current = root;
Node parent = root;
boolean isLeftChild = true;
//找到被删除的节点,并标识该节点是否为左节点
while (current.index != key) {
parent = current;
if (key < current.index) {
isLeftChild = true;
current = current.leftNode;
} else {
isLeftChild = false;
current = current.rightNode;
}
if (current == null) {
return false;
}
}
//第一种情况,删除节点为子节点
if (current.leftNode == null && current.rightNode == null) {
if (current == root) {
root = null;
} else {
if (isLeftChild) {
parent.leftNode = null;
} else {
parent.rightNode = null;
}
}
} else if ((current.leftNode != null && current.rightNode == null) || (current.leftNode == null && current.rightNode != null)) {
//第二中情况,删除节点只包含一个子节点,则将子节点移动动当前节点中
if (current.rightNode == null) {//删除的节点的左节点有值,右节点为空
if (root == current) {
root = current.leftNode;
} else {
if (isLeftChild) {
parent.leftNode = current.leftNode;
} else {
parent.rightNode = current.leftNode;
}
}
} else {//删除的节点的右节点有值,左节点为空
if (root == current) {
root = current.rightNode;
} else {
if (isLeftChild) {
parent.leftNode = current.rightNode;
} else {
parent.rightNode = current.rightNode;
}
}
}
} else if (current.leftNode != null && current.rightNode != null) {
//第三种情况,删除节点中有左右两个节点
//找到后继节点
Node processer = processer(current);
if (current == root) {//删除是根节点,则
root = processer;
} else {
if (isLeftChild) {
parent.leftNode = processer;
} else {
parent.rightNode = processer;
}
}
//选中的节点的左节点与删除节点的左节点相连
processer.leftNode = current.leftNode;
}
return true;
}
//找到后继节点
private Node processer(Node delNode) {
Node parent = delNode;
Node success = delNode;
Node current = delNode.rightNode;
while (current != null) {
// 后继节点父节点首先保存后继节点的状态
parent = current;
success = current;
// 后继节点 不断的向左更新
current = current.leftNode;
}
// 假如我们找到的后继节点不直接是 要删除节点的右节点 而是在其右节点那条子树上面最小的一个节点
if (success != delNode.rightNode) {
//后继节点的父节点断开其与后继节点左边的引用,重新连接上后继节点的右子节点(因为后继节点是没有左子节点的,锁以要保存之前树的状态,还要把后继节点的右子节点处理一下,不管 其存在不存在)
parent.leftNode = success.rightNode;
// 这时候后继节点的右边已经空了 上一条语句已经将其给了自己父节点的左子节点 然后让后继节点的右边 连接要删除节点的右子树
success.rightNode = delNode.rightNode;
}
return success;
}
二叉查找树 Java实现的更多相关文章
- 数据结构实现(四)二叉查找树java实现
转载 http://www.cnblogs.com/CherishFX/p/4625382.html 二叉查找树的定义: 二叉查找树或者是一颗空树,或者是一颗具有以下特性的非空二叉树: 1. 若左子树 ...
- 递归的二叉查找树Java实现
package practice; public class TestMain { public static void main(String[] args) { int[] ao = {50,18 ...
- 二叉查找树--java
package com.test.tree; public class BinarySearchTree<T extends Comparable<? super T>> { ...
- LeetCode96_Unique Binary Search Trees(求1到n这些节点能够组成多少种不同的二叉查找树) Java题解
题目: Given n, how many structurally unique BST's (binary search trees) that store values 1...n? For e ...
- Spark案例分析
一.需求:计算网页访问量前三名 import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} /* ...
- 数据结构笔记--二叉查找树概述以及java代码实现
一些概念: 二叉查找树的重要性质:对于树中的每一个节点X,它的左子树任一节点的值均小于X,右子树上任意节点的值均大于X. 二叉查找树是java的TreeSet和TreeMap类实现的基础. 由于树的递 ...
- Java for LintCode 验证二叉查找树
给定一个二叉树,判断它是否是合法的二叉查找树(BST) 一棵BST定义为: 节点的左子树中的值要严格小于该节点的值. 节点的右子树中的值要严格大于该节点的值. 左右子树也必须是二叉查找树. ...
- 二叉查找树的Java实现
为了克服对树结构编程的恐惧感,决心自己实现一遍二叉查找树,以便掌握关于树结构编程的一些技巧和方法.以下是基本思路: [1] 关于容器与封装.封装,是一种非常重要的系统设计思想:无论是面向过程的函数,还 ...
- 二叉查找树(三)之 Java的实现
概要 在前面分别介绍了"二叉查找树的相关理论知识,然后给出了二叉查找树的C和C++实现版本".这一章写一写二叉查找树的Java实现版本. 目录 1. 二叉树查找树2. 二叉查找树的 ...
随机推荐
- vc中使用SendMessage正确发送自定义消息的方法
最近在用VC2008做开发,后来由于要用到消息的发送,而且需要自定义消息,在网上查找了很多例子,根据他们所说的,虽然大致都差不多,但是基本上没有 一个能完全做出来的.要知道VC编程有一个小地方出错,都 ...
- springmvc 开涛 生产者/消费者
媒体类型: text/html, text/plain, text/xml image/gif, image/jpg, image/png application/x-www-form-urlenco ...
- Python学习-17.Python中的错误处理(二)
错误是多种多样的,在 except 语句中,可以捕获指定的异常 修改代码如下: import io path = r'' mode = 'w' try: file = open(path,mode) ...
- Tmux与Oh-my-zsh环境整合
在Mac客户端配置好oh-my-zsh后,安装了tmux应用,但是每次进入tmux都会提示以下警告信息,虽然并没有实际上的影响,但是还是感觉每次弹出窗口后会很闹心,所以采用如下配置进行解决. 报错 ...
- java多线程 —— 两种实际应用场景模拟
最近做的偏向并发了,因为以后消息会众多,所以,jms等多个线程操作数据的时候,对共享变量,这些要很注意,以防止发生线程不安全的情况. (一) 先说说第一个,模拟对信息的发送和接收.场景是这样的: 就像 ...
- 探索TFS Git 库文件换行(CRLF)的处理方式
(2018.12.29 更新,增加Git处理方式) 在计算机的技术中,所有文本信息都会涉及换行的问题.例如,你在键盘上敲击一次Enter(回车)键,系统将在文本重增加一行,实际上系统已经在文件中插入了 ...
- MVC批量上传文件
初始图片: 选中图片后 ---------------------------------------------------------------------------------- 前端代码 ...
- The rapid development platform upgrade, leave the time to yourself, the work is lost to the soft platform
Bring me back to your home. Please leave your work behind! Soft agile development framework V7.0 new ...
- akka开发(一)HelloWorld
package com.hfi.helloakka; import akka.actor.ActorRef; import akka.actor.Props; import akka.actor.Un ...
- HDU4622 Reincarnation
题目链接:戳我 因为对应的很多询问,所以我们一定要将每一种询问先处理出来,然后O(1)查询. 至于怎么处理出来子串的子串呢? 我们固定左端点,然后依次加入子串即可.然后统计的时候直接统计last那一个 ...