计算机里面的数据结构 树 在计算机存储领域应用作用非常大,我之前也多次强调多磁盘的存取速度是目前计算机飞速发展的一大障碍,计算机革命性的的下一次飞跃就是看硬盘有没有质的飞跃,为什么这么说?因为磁盘是永久性存储设备(在相当长的时间内都可以用),就这一点虽然内存在性能方面优势巨大但是保存信息和数据还是要靠磁盘。

数最成功的要数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二叉搜索树原理

* 二叉搜索树操作(节点的左边小于节点值,而右边大于节点值)
* @author www.mojxtang.website
*
*/
publicclassBinaryTree{
 
/**
* 根节点
*/
privateNode root;
/**
* 查找一个节点
* @param key 关键字 ID值
* @return
*/
publicNode find(intkey){
Node current=root;
while(current.getId()!=key){
//如果key小于当前节点,就去找左边比当前小的节点
if(current.getId()>key){
current=current.getLeftChild();
//如果key大于当前节点,就去找右边比当前大的节点
}elseif(current.getId()<key){
current=current.getRightChild();
}
if(current==null){
returnnull;
}
}
returncurrent;
}
/**
* 插入节点
* @param id
* @param data
*/
publicvoidinsert(intid,intdata){
Node newNode=newNode(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
*/
publicvoidpreOrder(Node node){
if(node!=null){
System.out.println(node.getId()+" - ");
preOrder(node.getLeftChild());
preOrder(node.getRightChild());
}
}
/**
* 中序--获取节点数据
* @param node
*/
publicvoidinOrder(Node node){
if(node!=null){
inOrder(node.getLeftChild());
System.out.println(node.getId()+" - ");
inOrder(node.getRightChild());
}
}
/**
* 后序--获取节点数据
* @param node
*/
publicvoidaftOrder(Node node){
if(node!=null){
aftOrder(node.getLeftChild());
aftOrder(node.getRightChild());
System.out.println(node.getId()+" - ");
}
}
/**
* 获取最小节点数据(使劲往左边找)
* @param node
*/
publicNode getMinNode(){
Node current=root;
Node minNode=null;
while(current!=null){
minNode=current;
current=current.getLeftChild();
}
returnminNode;
}
/**
* 获取最大节点数据(使劲往右边找)
* @param node
*/
publicNode getMaxNode(){
Node current=root;
Node maxNode=null;
while(current!=null){
maxNode=current;
current=current.getRightChild();
}
returnmaxNode;
}
/**
* 删除一个节点(删除节点有两个子节点的时候,要用它的中序后继来代替该节点)
     * 算法是:找到被删除节点的右子节点,然后查找这个右子节点下的最后一个左子节点,
     * 也就是这颗子树的最小值节点,这就是被删除节点的中序后继节点。
     * 三种情况:
     *   1.没有子节点
     *   2.只有一个子节点
     *   3.有两个子节点
* @param key
* @return
*/
publicbooleandelete(intkey){
//先找到需要删除的节点
Node current=root;
Node parent=root;
booleanisLeftNode=true;
while(current.getId()!=key){//没有找到
parent=current;
if(current.getId()>key){//当前节点大于key,往左找
isLeftNode=true;
current=current.getLeftChild();
}elseif(current.getId()<key){//当前节点小于key,往右找
isLeftNode=false;
current=current.getRightChild();
}
if(current==null){
returnfalse;
}
}
 
//1.没有子节点
if(current.getLeftChild()==null&&current.getRightChild()==null){
this.noChild(parent,current,isLeftNode);
}
//2.只有一个节点
elseif(current.getRightChild()==null){
this.oneLeftNode(parent,current,isLeftNode);
}
elseif(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());
 
}
returntrue;
}
/**
* 找到要删除节点的中序后继节点
* 算法是:找到被删除节点的右子节点,然后查找这个右子节点下的最后一个左子节点,
     *    也就是这颗子树的最小值节点,这就是被删除节点的中序后继节点。
* @param current
* @return
*/
privateNode 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());
}
returnsuccessor;
}
privatevoidoneRightNode(Node parent,Node current,booleanisLeftNode){
if(current==root){
root=current.getRightChild();
}else{
if(isLeftNode){
parent.setLeftChild(current.getRightChild());
}else{
parent.setRightChild(current.getRightChild());
}
}
}
privatevoidoneLeftNode(Node parent,Node current,booleanisLeftNode){
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
*/
privatevoidnoChild(Node parent,Node current,booleanisLeftNode){
//如果是根节点
if(current==root){
root=null;
}else{
//这里为什么把父节点的左右值空?
if(isLeftNode){
parent.setLeftChild(null);
}else{
parent.setRightChild(null);
}
}
}
 
publicstaticvoidmain(String[]args){
BinaryTree tree=newBinaryTree();
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二叉搜索树原理与实现的更多相关文章

  1. Java二叉搜索树实现

    树集合了数组(查找速度快)和链表(插入.删除速度快)的优点 二叉树是一种特殊的树,即:树中的每个节点最多只能有两个子节点 二叉搜索树是一种特殊的二叉树,即:节点的左子节点的值都小于这个节点的值,节点的 ...

  2. AVL平衡二叉搜索树原理及各项操作编程实现

    C语言版 #include<stdio.h> #include "fatal.h" struct AvlNode; typedef struct AvlNode *Po ...

  3. C++ 二叉搜索树原理及其实现

    首先是概念:二叉搜索树又称二叉排序树,它具有以下的性质: 若是左子树不为空,则左子树上所有节点的值小于根节点的值 若是右子树不为空,则右子树上所有结点的值大于根节点的值 二叉搜索树的左右子树也是二叉搜 ...

  4. Java 二叉搜索树 实现和学习

    /** * <html> * <body> * <P> Copyright 1994 JsonInternational</p> * <p> ...

  5. java 二叉搜索树

    java二叉查找树实现: 二叉查找树,上图:比根节点小者在其左边,比根节点大者在其右边. 抽象数据结构,上代码: /** * 二叉查找树数据结构(非线程安全): * 范型类型须实现Comparable ...

  6. 【算法学习】AVL平衡二叉搜索树原理及各项操作编程实现(C语言)

    #include<stdio.h> #include "fatal.h" struct AvlNode; typedef struct AvlNode *Positio ...

  7. 二叉搜索树详解(Java实现)

    1.二叉搜索树定义 二叉搜索树,是指一棵空树或者具有下列性质的二叉树: 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值: 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根 ...

  8. 【算法与数据结构】二叉搜索树的Java实现

    为了更加深入了解二叉搜索树,博主自己用Java写了个二叉搜索树,有兴趣的同学可以一起探讨探讨. 首先,二叉搜索树是啥?它有什么用呢? 二叉搜索树, 也称二叉排序树,它的每个节点的数据结构为1个父节点指 ...

  9. Java实现二叉搜索树的添加,前序、后序、中序及层序遍历,求树的节点数,求树的最大值、最小值,查找等操作

    什么也不说了,直接上代码. 首先是节点类,大家都懂得 /** * 二叉树的节点类 * * @author HeYufan * * @param <T> */ class Node<T ...

随机推荐

  1. “全栈2019”Java异常第十五章:异常链详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java异 ...

  2. CentOS7安装weblogic集群思路梳理

    以前经常用weblogic集群,但是却没有仔细想过要实现它.这不,前两天成功安装了weblogic集群,现在将其思路整理下.防止日后自己忘掉了. 一.安装weblogic10.3.6 1. 在官网下载 ...

  3. Vim 中的持久撤消

    Vim中的持久撤消 阅读文本大约需要俩分钟. 在 Vim 中像其他文本编辑器一样,你可以在当前会话中执行 "撤销/重做" .当一旦会话关闭,则你需要重新打开一个新文章,运行撤销将不 ...

  4. instanceof,isinstance,isAssignableFrom,asSubclass的区别

    1,isAssignableFrom():是字节码对象的方法 是用来判断一个类的字节码对象和另一个类的字节码对象是否相同或是子类或接口. assignable英 [ə,sainəbl]美 [ə,sai ...

  5. vue封装插件并发布到npm上

    vue封装插件并发布到npm上 项目初始化 首先,要创建项目,封装vue的插件用webpack-simple很合适,vue init webpack-simple 项目名称此命令创建我们的项目的目录, ...

  6. SDN定义网络

    http://edu.51cto.com/course/course_id-4466.html http://edu.51cto.com/course/course_id-4497.html

  7. mysql工具——mysqlcheck(MYISAM)

    基本介绍 演示: 使用optimize的时候,可能会出现 Table does not support optimize, doing recreate + analyze instead 这时候参考 ...

  8. hiho# 1394最小路径覆盖 网络流拆点

    题目传送门 思路: 观察到路径上除了终点起点以外的每个点出度和入度都为1,和网络流的拆点很像,所以就把每个点都拆成两个点,若存在一条路径$(u,v)$,则建一条$(u,v+n,1)$的边,然后求出最大 ...

  9. Mac下使用crontab来实现定时任务

    说明: 1.Linux和Mac下操作crontab都是一致的 2.配置文件都在/etc/crontab下,如果没有就创建. 3.测试发现直接使用crontab -e命令创建的定时任务是放在临时文件夹的 ...

  10. BigDecimal加减乘除运算(转)

    java.math.BigDecimal.BigDecimal一共有4个够造方法,让我先来看看其中的两种用法: 第一种:BigDecimal(double val) Translates a doub ...