定义:

一棵二叉查找树是一棵二叉树,每个节点都含有一个Comparable的键(以及对应的值)。

每个节点的键都大于左子树中任意节点的键而小于右子树中任意节点的键。

树的术语:

Name Function
路径 顺着连接点的边从一个节点走向另一个节点,所经过的节点的顺序排列就称为路径。
树顶端的节点就称为根,一棵树只有一个根,如果要把一个节点和边的集合定义为树,那么从根到其他任何一个节点都必须有一条路径。
父节点 每个节点(除了根)都恰好有一条边向上连接到另一个节点,上面的节点就称为下面节点的“父节点”。
子节点 每个节点都可能有一条或多条边向下连接其他节点,下面的这些节点就称为它的“子节点”。
叶节点 没有子节点的节点称为“叶子节点”或简称“叶节点”。树只能有一个根,但是可以有很多叶节点。
子树 每个节点都可以作为子树的根,它和它所有的子节点,子节点的子节点等都含在子树中。
访问 当程序控制流程到达某个节点的时候,就称为“访问”这个节点,通常是为了在这个节点处执行某种操作,例如查看节点某个数据字段的值或者显示节点。
遍历 遍历树意味着要遵循某种特定的顺序访问树中的所有节点。
一个节点的层数是指从根开始到这个节点有多少“代”。
关键字 可以看到,对象中通常会有一个数据域被指定为关键字值。这个值通常用于查询或者其他操作。
二叉树 如果树中的每个节点最多只能有两个子节点,这样的树就称为“二叉树”。

性质:

  1. 若它的左子树不为空,则左子树上的所有节点的值都小于它的根节点的值;
  2. 若它的右子树不为空,则右子树上所有节点的值都大于它的根节点的值;
  3. 其他的左右子树也分别为二叉查找树;
  4. 二叉查找树是动态查找表,在查找的过程中可见添加和删除相应的元素,在这些操作中需要保持二叉查找树的以上性质。

根据其二叉树的特性,节点类如下:

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;
}
}
}
}

遍历节点,中序遍历.

  1. 调用自身来遍历节点的左子树
  2. 访问这个节点
  3. 掉用自身来遍历节点的右子树
public void inOrder(Node localRoot) {
if (localRoot != null) {
inOrder(localRoot.leftNode);
System.out.println("索引:" + localRoot.index + ",值:" + localRoot.data);
inOrder(localRoot.rightNode);
}
}

删除节点,分三种情况:

  1. 删除节点没有子节点,那么将父节点的左节点或者是右节点设置为空
  2. 删除节点只有一个子节点,删除该节点后,该节点的子节点变为父节点的子节点,如果删除节点时父节点的左节点,那么父节点的左节点指向该节点的子节点,反之则右节点指向删除节点的子节点。
  3. 删除节点有两个字节点,删除了该节点后,则需要选择一个后继节点,并且还不破坏该二叉树的特性(左节点要小于右节点),一般是从删除节点的右节点中找到一个后继节点,而这个节点是右子树的最小值。
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实现的更多相关文章

  1. 数据结构实现(四)二叉查找树java实现

    转载 http://www.cnblogs.com/CherishFX/p/4625382.html 二叉查找树的定义: 二叉查找树或者是一颗空树,或者是一颗具有以下特性的非空二叉树: 1. 若左子树 ...

  2. 递归的二叉查找树Java实现

    package practice; public class TestMain { public static void main(String[] args) { int[] ao = {50,18 ...

  3. 二叉查找树--java

    package com.test.tree; public class BinarySearchTree<T extends Comparable<? super T>> { ...

  4. 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 ...

  5. Spark案例分析

    一.需求:计算网页访问量前三名 import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} /* ...

  6. 数据结构笔记--二叉查找树概述以及java代码实现

    一些概念: 二叉查找树的重要性质:对于树中的每一个节点X,它的左子树任一节点的值均小于X,右子树上任意节点的值均大于X. 二叉查找树是java的TreeSet和TreeMap类实现的基础. 由于树的递 ...

  7. Java for LintCode 验证二叉查找树

    给定一个二叉树,判断它是否是合法的二叉查找树(BST) 一棵BST定义为: 节点的左子树中的值要严格小于该节点的值.    节点的右子树中的值要严格大于该节点的值.    左右子树也必须是二叉查找树. ...

  8. 二叉查找树的Java实现

    为了克服对树结构编程的恐惧感,决心自己实现一遍二叉查找树,以便掌握关于树结构编程的一些技巧和方法.以下是基本思路: [1] 关于容器与封装.封装,是一种非常重要的系统设计思想:无论是面向过程的函数,还 ...

  9. 二叉查找树(三)之 Java的实现

    概要 在前面分别介绍了"二叉查找树的相关理论知识,然后给出了二叉查找树的C和C++实现版本".这一章写一写二叉查找树的Java实现版本. 目录 1. 二叉树查找树2. 二叉查找树的 ...

随机推荐

  1. 通过hbase实现日志的转存(MR AnalyserLogDataRunner和AnalyserLogDataMapper)

    操作代码(提前启动集群(start-all.sh).zookeeper(zkServer.sh start).启动历史任务服务器(mr-jobhistory-daemon.sh start histo ...

  2. D3_book 11.3 force

    <!-- pie example --> <!DOCTYPE html> <meta charset="utf-8"> <style> ...

  3. Android-Git命令行操作

    Git命令行操作,在Mac上使用的话,Mac会自带了Git,直接在终端或者iTerm都可以执行Git命令操作:    Git命令行操作,在Windows系统电脑上使用的话,需要安装Git,安装好Git ...

  4. Linux Mint 楷体问题

    很多人都遇见过刚装完的 Linux Mint 界面字体是黑体,然后莫名其妙就变成楷体的问题. 先不说技术层面的原因,只说怎么解决. 造成这种情况,多数是安装输入法或其他某种软件的时候,同时安装了 AR ...

  5. asp.net excel导出功能

    以下是我在项目开发中所做的关于Excel导出功能,不足之处还望大家指正,相互学习 protected void btn_Export_Click(object sender, EventArgs e) ...

  6. Cookie操作-----Selenium快速入门(十一)

    什么是cookie?顾名思义,就是饼干,小甜饼.而根据读音则是“曲奇”的意思,相信不少的人都吃过.而在网络上,cookie是指浏览器在本地的一种少量数据的存储方式.例如,我们常见的,登陆的时候有个ch ...

  7. Lucene.net 全文检索文件

    using Lucene.Net.Analysis; using Lucene.Net.Analysis.Tokenattributes; using Lucene.Net.Documents; us ...

  8. qt linux下配置安装

    linux版本: qt卸载: 1. 先找到qt的安装位置: 2.然后执行其下面的文件MaintenanceTool: 3. 然后会出现图形界面: 卸载完成. 安装qt 下载地址: https://ww ...

  9. 构造回文-C++实现

    腾讯2017暑期实习生招聘笔试题……做了一个世纪才做出来 //腾讯2017暑期实习生招聘第一道题.做了一个世纪才做出来………………太菜了 /** 题目: 给定一个字符串s,你可以从中删除一些字符,使得 ...

  10. brew - 更换国内源

    brew如果不换成国内源,安装软件时候可能会出问题,不是安装不了就是速度很慢,所以使用它,更换国内游是比较好的选择! 我更换的是清华大学开源软件镜像站,打开shell窗口,依次执行下面命令: cd & ...