二叉查找树 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. 二叉查找树的 ...
随机推荐
- 洛谷P1600 天天爱跑步(线段树合并)
小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 nn ...
- jersey学习笔记
最近一个项目用到了jersey,我只是负责前端.也借此机会好好了解一下REST webservice及一大推名词. http://redhacker.iteye.com/blog/1914105 1. ...
- jquery添加select option两种代码思路比较
功能需求:在客户选择了check_in_date和check_out_date之后,将在check_in_date至check_out_date的promotions中自动添加符合条件的promoti ...
- Android-JVM中的多线程&垃圾回收
Java语言是为数不多支持多线程技术的编程语言,而这多线程就不得不提到JVM虚拟机 先看代码案例:(JVM收垃圾) package android.java.thread; class Demo { ...
- Android-自定义ListView下拉刷新与上拉加载
效果图: 第一步:编写需要在ListView中增加头加载的布局文件,与底部加载的布局文件: 头布局文件: <?xml version="1.0" encoding=" ...
- 启用Nginx目录浏览功能的方法
location / { root /data/www/file //指定实际目录绝对路径: autoindex on; ...
- LeetCode142:Linked List Cycle II
题目: Given a linked list, return the node where the cycle begins. If there is no cycle, return null. ...
- .netcore部署centos
前言:最近公司有个项目用 .netcore开发的项目,然后闲的没事就研究如果发布到Linux系统上 需要安装的插件以及支撑架构 1.dotnetSDK 2.jexus Jexus 是Linux平台上 ...
- Wpf 导出CSV文件
/// <summary> /// 将DataTable中数据写入到CSV文件中 /// </summary> /// <param name="dt" ...
- XML Web Service架构图