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

数最成功的要数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. Java中运算符“|”和“||”以及“&”和“&&”区别

    1.“|”运算符:不论运算符左侧为true还是false,右侧语句都会进行判断,下面代码 int a =1,b=1; if(a++ == 1 | ++b == 2) System.out.printl ...

  2. SpringMvc拦截器运行原理。

    首先,先简单的说一下怎么配置SpringMvc的拦截器. 分两步,第一步先定义一个类,实现HandlerInterceptor接口. import javax.servlet.http.HttpSer ...

  3. Commands that may modify the data set are disabled, because this instance is configured to report errors during writes if RDB snapshotting fails (stop-writes-on-bgsave-error option)

    今天运行Redis时发生错误,错误信息如下: org.springframework.dao.InvalidDataAccessApiUsageException: MISCONF Redis is ...

  4. vim中常用的命令

    1.光标的命令 gg 移到第一行位置 G 移到最后一行 o       行首 $       行末 nk 向上移动n行 nj 向下移动n行 nh 向左移动n列 nl 向右移动n列 ctrl+f     ...

  5. linux ln 命令,相当于windows快捷方式

    ln -s 源文件 目标文件. ln -s  **  **,它只会在你选定的位置上生成一个文件的镜像,不会占用磁盘空间, 硬链接ln ** **,没有参数-s, 它会在你选定的位置上生成一个和源文件大 ...

  6. service iptables xxx无效命令的情况下,如何启动/重启iptables

    最近在CentOS 7.6下使用service iptables xxx相关命令,提示如下错误:The service command supports only basic LSB actions ...

  7. Mac 10.12安装SVN工具SmartSVM 7.6

    说明:SVN工具没有最好的,只有用的最顺手的. 下载: (链接: https://pan.baidu.com/s/1dFGqEsT 密码: uyjx)

  8. Ubuntu 安装ftp

    Ubuntu 用vsftpd 配置FTP服务器 网上的文章好难懂啊..只想要简单粗暴,弄好能用就行啊,复杂的以后研究不行吗...折腾好久,其实弄出来能用不就这么点内容吗... 本文在Ubuntu Se ...

  9. AngularJS国际化配置

    AngularJS国际化配置 下载angular-translate 下载zip包:https://github.com/angular-translate/bower-angular-transla ...

  10. PHP之string之explode()函数使用

    explode (PHP 4, PHP 5, PHP 7) explode - Split a string by string explode - 使用一个字符串分割另一个字符串 Descripti ...