java二叉搜索树原理与实现

计算机里面的数据结构 树 在计算机存储领域应用作用非常大,我之前也多次强调多磁盘的存取速度是目前计算机飞速发展的一大障碍,计算机革命性的的下一次飞跃就是看硬盘有没有质的飞跃,为什么这么说?因为磁盘是永久性存储设备(在相当长的时间内都可以用),就这一点虽然内存在性能方面优势巨大但是保存信息和数据还是要靠磁盘。
数最成功的要数B+tree和LSM-tree了,在关系型数据库和非关系型数据库(Nosql)可谓是处于主导地位,RocksDB目前在nosql和newsql中都大放光彩,其存储引擎就是LSM-tree,还有hbase,levelDB等。作为树的变种在存储领域不断突破性能的瓶颈。

/**
* 二叉树的节点
* @author www.mojxtang.website
*
*/
public class Node {
/**
* 关键字/索引(识别数据用)
*/
private int id;
/**
* 数据项(可以是任意对象T,也可以表示多个数据项)
*/
private int data;
private Node leftChild;
private Node rightChild;
public Node(int id, int data) {
super();
this.id = id;
this.data = data;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public Node getLeftChild() {
return leftChild;
}
public void setLeftChild(Node leftChild) {
this.leftChild = leftChild;
}
public Node getRightChild() {
return rightChild;
}
public void setRightChild(Node rightChild) {
this.rightChild = rightChild;
}
@Override
public String toString() {
return "Node [id=" + id + ", data=" + data + ", leftChild=" + leftChild + ", rightChild=" + rightChild + "]";
}
}
/**
* 二叉搜索树操作(节点的左边小于节点值,而右边大于节点值)
* @author www.mojxtang.website
*
*/
public class BinaryTree {
/**
* 根节点
*/
private Node root;
/**
* 查找一个节点
* @param key 关键字 ID值
* @return
*/
public Node find(int key) {
Node current = root;
while(current.getId() != key) {
//如果key小于当前节点,就去找左边比当前小的节点
if (current.getId() > key) {
current = current.getLeftChild();
//如果key大于当前节点,就去找右边比当前大的节点
}else if (current.getId() < key) {
current = current.getRightChild();
}
if (current == null) {
return null;
}
}
return current;
}
/**
* 插入节点
* @param id
* @param data
*/
public void insert(int id,int data) {
Node newNode = new Node(id, data);
if (root == null) {
root = newNode;
}else {
//为什么这里要存放current=root和parent=null这两个节点对象?
Node current = root;
Node parent = null;
while (true) {
parent = current;
//如果新节点小于当前节点,我们就去左子节点找
if (id < current.getId()) {//左边
current = current.getLeftChild();
//如果没有左子节点,说明我们找到了,可以将新节点插入到此
if (current == null) {
parent.setLeftChild(newNode);
return;
}
}else {//右边
current = current.getRightChild();
//如果没有右子节点,说明我们找到了,可以将新节点插入到此
if (current == null) {
parent.setRightChild(newNode);
return;
}
}
}
}
}
/**
* 前序---获取节点数据
* @param node
*/
public void preOrder(Node node) {
if (node !=null) {
System.out.println(node.getId()+" - ");
preOrder(node.getLeftChild());
preOrder(node.getRightChild());
}
}
/**
* 中序--获取节点数据
* @param node
*/
public void inOrder(Node node) {
if (node != null) {
inOrder(node.getLeftChild());
System.out.println(node.getId()+" - ");
inOrder(node.getRightChild());
}
}
/**
* 后序--获取节点数据
* @param node
*/
public void aftOrder(Node node) {
if (node != null) {
aftOrder(node.getLeftChild());
aftOrder(node.getRightChild());
System.out.println(node.getId()+" - ");
}
}
/**
* 获取最小节点数据(使劲往左边找)
* @param node
*/
public Node getMinNode() {
Node current = root;
Node minNode = null;
while (current != null) {
minNode = current;
current = current.getLeftChild();
}
return minNode;
}
/**
* 获取最大节点数据(使劲往右边找)
* @param node
*/
public Node getMaxNode() {
Node current = root;
Node maxNode = null;
while (current != null) {
maxNode = current;
current = current.getRightChild();
}
return maxNode;
}
/**
* 删除一个节点(删除节点有两个子节点的时候,要用它的中序后继来代替该节点)
* 算法是:找到被删除节点的右子节点,然后查找这个右子节点下的最后一个左子节点,
* 也就是这颗子树的最小值节点,这就是被删除节点的中序后继节点。
* 三种情况:
* 1.没有子节点
* 2.只有一个子节点
* 3.有两个子节点
* @param key
* @return
*/
public boolean delete(int key) {
//先找到需要删除的节点
Node current = root;
Node parent = root;
boolean isLeftNode = true;
while (current.getId() != key) {//没有找到
parent = current;
if (current.getId() > key) {//当前节点大于key,往左找
isLeftNode = true;
current = current.getLeftChild();
}else if (current.getId() < key) {//当前节点小于key,往右找
isLeftNode = false;
current = current.getRightChild();
}
if (current == null) {
return false;
}
}
//1.没有子节点
if (current.getLeftChild() == null && current.getRightChild() == null) {
this.noChild(parent, current, isLeftNode);
}
//2.只有一个节点
else if (current.getRightChild() == null) {
this.oneLeftNode(parent, current, isLeftNode);
}
else if (current.getLeftChild() == null) {
this.oneRightNode(parent, current, isLeftNode);
}
//3.有两个子节点
else {
//找到中序后继节点
Node successor = this.getSuccessor(current);
if (current == root) {
root = successor;
}else {
if (isLeftNode) {
parent.setLeftChild(successor);
}else {
parent.setRightChild(successor);
}
}
//设置后继节点的左节点
successor.setLeftChild(current.getLeftChild());
}
return true;
}
/**
* 找到要删除节点的中序后继节点
* 算法是:找到被删除节点的右子节点,然后查找这个右子节点下的最后一个左子节点,
* 也就是这颗子树的最小值节点,这就是被删除节点的中序后继节点。
* @param current
* @return
*/
private Node getSuccessor(Node delNode) {
//这里为什么记录三个节点对象?
Node successor = delNode;
Node successorParent = delNode;
Node current = delNode.getRightChild();
//查找最后一个左子节点
while (current != null) {
successorParent = successor;
successor = current;
current = current.getLeftChild();
}
if (successor != delNode.getLeftChild()) {
successorParent.setLeftChild(successor.getRightChild());
successor.setRightChild(delNode.getRightChild());
}
return successor;
}
private void oneRightNode(Node parent,Node current,boolean isLeftNode) {
if (current == root) {
root = current.getRightChild();
}else {
if (isLeftNode) {
parent.setLeftChild(current.getRightChild());
}else {
parent.setRightChild(current.getRightChild());
}
}
}
private void oneLeftNode(Node parent,Node current,boolean isLeftNode) {
if (current == root) {
root = current.getLeftChild();
}else {
//这里为什么设置父节点的左右都设置为current的左节点?
if (isLeftNode) {
parent.setLeftChild(current.getLeftChild());
}else {
parent.setRightChild(current.getLeftChild());
}
}
}
/**
* 没有子节点
* @param parent
* @param current
* @param isLeftNode
*/
private void noChild(Node parent,Node current,boolean isLeftNode) {
//如果是根节点
if (current == root) {
root = null;
}else {
//这里为什么把父节点的左右值空?
if (isLeftNode) {
parent.setLeftChild(null);
}else {
parent.setRightChild(null);
}
}
}
public static void main(String[] args) {
BinaryTree tree = new BinaryTree();
tree.insert(6, 212);
tree.insert(5, 211);
tree.insert(8, 221);
tree.insert(3, 321);
tree.insert(7, 421);
tree.insert(9, 521);
System.out.println(tree.root.toString());
tree.inOrder(tree.find(6));
System.out.println(tree.getMinNode());
System.out.println(tree.getMaxNode());
tree.delete(5);
System.out.println(tree.root.toString());
}
}
二叉搜索树是所有树结构的开始模型,它最明显的特性就是节点的左子节点比该节点大,比该节点的右子节点小,形成的树大家可以想想,我们可以说他“有序”。
大家不妨思考为什么在计算机里面会用这样的数据结构来做索引其实二叉树是没有节点的左右大小之分的,那为什么我们非要把左右节点搞出来个大小呢???
二叉搜索树复杂的就是删除大家看完代码会发现,数据的删除其实就是替换和制空的过程,这个过程有个关键性的操作就是存放临时变量,我在代码块里面备注的几个为什么大家思考思考是不是这个道理。
文章地址:java二叉搜索树原理
java二叉搜索树原理与实现的更多相关文章
- Java二叉搜索树实现
树集合了数组(查找速度快)和链表(插入.删除速度快)的优点 二叉树是一种特殊的树,即:树中的每个节点最多只能有两个子节点 二叉搜索树是一种特殊的二叉树,即:节点的左子节点的值都小于这个节点的值,节点的 ...
- AVL平衡二叉搜索树原理及各项操作编程实现
C语言版 #include<stdio.h> #include "fatal.h" struct AvlNode; typedef struct AvlNode *Po ...
- C++ 二叉搜索树原理及其实现
首先是概念:二叉搜索树又称二叉排序树,它具有以下的性质: 若是左子树不为空,则左子树上所有节点的值小于根节点的值 若是右子树不为空,则右子树上所有结点的值大于根节点的值 二叉搜索树的左右子树也是二叉搜 ...
- Java 二叉搜索树 实现和学习
/** * <html> * <body> * <P> Copyright 1994 JsonInternational</p> * <p> ...
- java 二叉搜索树
java二叉查找树实现: 二叉查找树,上图:比根节点小者在其左边,比根节点大者在其右边. 抽象数据结构,上代码: /** * 二叉查找树数据结构(非线程安全): * 范型类型须实现Comparable ...
- 【算法学习】AVL平衡二叉搜索树原理及各项操作编程实现(C语言)
#include<stdio.h> #include "fatal.h" struct AvlNode; typedef struct AvlNode *Positio ...
- 二叉搜索树详解(Java实现)
1.二叉搜索树定义 二叉搜索树,是指一棵空树或者具有下列性质的二叉树: 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值: 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根 ...
- 【算法与数据结构】二叉搜索树的Java实现
为了更加深入了解二叉搜索树,博主自己用Java写了个二叉搜索树,有兴趣的同学可以一起探讨探讨. 首先,二叉搜索树是啥?它有什么用呢? 二叉搜索树, 也称二叉排序树,它的每个节点的数据结构为1个父节点指 ...
- Java实现二叉搜索树的添加,前序、后序、中序及层序遍历,求树的节点数,求树的最大值、最小值,查找等操作
什么也不说了,直接上代码. 首先是节点类,大家都懂得 /** * 二叉树的节点类 * * @author HeYufan * * @param <T> */ class Node<T ...
随机推荐
- “全栈2019”Java第一百章:局部内部类可以实现接口吗?
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- 工作中常用Linux命令
建立软链接 ln -s 例:ln -s b a 解释:把文件夹a和文件夹b关联起来,访问文件夹a,实际访问的是问价夹b 删除软连接 rm -rf a 直接删掉a文件夹跟a和b的软连接. ...
- BZOJ 4719--天天爱跑步(LCA&差分)
4719: [Noip2016]天天爱跑步 Time Limit: 40 Sec Memory Limit: 512 MBSubmit: 1464 Solved: 490[Submit][Stat ...
- ASP.NET MVC 发布后 IE 访问出现布局错乱问题
ASP.NET MVC 网页debug启动跑一切正常,[Chrome],[FireFox],[Edge],[IE11] 发布后,使用机器名访问,[IE11]出现布局不正常的问题, 在head里:加↓可 ...
- Markdown入门简介
参考 http://sspai.com/25137 作者: Te_Lee 文章来源: 少数派 Markdown入门简介(使用工具Haroopad) 一.使用的工具----haroopad(http:/ ...
- mybatis一级缓存与二级缓存的原理
1.mybatis中的缓存是在mybatis框架中的Executor中来实现的,我们来看一下Executor的继承图 2.通过以上类图我们可以发现Executor接口下有两大实现类BaseExecut ...
- [性能测试]:关于消费类ISO8583协议脚本的开发
一,要发送的报文,转化成16进制的,报文如下 "\x01\x52"//报文长度338 "\x60\x00\x24\x00\x00"//TPDU "\x ...
- windows server 2012 valid key
好吧,网页三剑客. 1, load disc iso 2,check ip settings, 3,net-inst-server-start 4,power Node, F2 4.1 F7 usbc ...
- 怎么搭建一个5T的私有云盘
视频 点击打开视频 下载地址 免费域名网址: https://www.freewebhostingarea.com/ ftp工具: http://t.cn/EXWxYUI oneindex: http ...
- centos7安装多媒体播放器SMPlayer
转自:https://wiki.centos.org/TipsAndTricks/MultimediaOnCentOS7 http://blog.chinaunix.net/xmlrpc.php?r= ...