Java实现二叉搜索树

定义

二叉排序树(Binary Sort Tree),又称二叉查找树(Binary Search Tree),亦称二叉搜索树。

结构

二叉搜索树的建立过程.
这里写图片描述

特点

二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
(3)左、右子树也分别为二叉排序树;

遍历

前序遍历

规则:若二叉树为空,则空操作返回,否则先访问根节点,再前序遍历左子树,再后序遍历右子树.
遍历结果:A-B-D-E-C-F-G-H

这里写图片描述

中序遍历

规则:若二叉树为空,则空操作返回,否则先从根节点开始(注意不是先访问根节点),再中序遍历左子树,然后是访问根节点,再中序遍历右子树.
遍历结果:D-B-E-A-F-C-G-H
这里写图片描述

后序遍历

规则:若二叉树为空,则空操作返回,否则先从根节点开始(注意不是先访问根节点),再中序遍历左子树,再中序遍历右子树,然后是访问根节点.
遍历结果:D-E-B-F-H-G-C-A
这里写图片描述
可以从图形和遍历方式推倒出遍历结果,也可以从遍历结果和遍历方式推倒出图形.

实例

创建节点

从上面的结构可以看出节点包括三个属性,节点值,左子节点引用和右子节点引用.

public class Node {
    //节点值
    int   val;
    //左子节点引用
    Node  leftChild;
    //右子节点引用
    Node  rightChild;

    public void  printNode() {
        System.out.println(val);
    }
}

创建二叉搜索树

该类只有一个私有属性root以及相关方法,该私有属性指向其根节点.
也可以添加size作为节点数.

public class BinaryTree {

    private Node root;
}

插入节点

这里写图片描述
比如在上面的二叉搜索树插入35的值:
1.从根节点开始比较,35<50,从左子树查找插入位置,
parent = current; current=root.leftChild(25);
2.35>25,说明在其右子树添加,
parent = current; current=root.leftChild(null);
此时current==null,因此新节点30就在该位置添加.
parent.rightChild = newNode(30);

public void insert(int data) {
        Node  newNode = new Node();
        newNode.val = data;     
        if(root == null) {
            //如果是第一个节点,也就是根节点为null,直接创建一个新的节点即可 
            root = newNode;
        }
        else {
            Node current = root;
            //current节点的父节点
            Node parent;
            //循环查找插入的位置
            while(true) {
                parent = current;
                //如果插入的值小于当前节点的值,从左子树查找
                if(data < current.val) {
                    current = current.leftChild;
                    //直到当前节点为null
                    if(current == null) {
                        //设置当前节点的父节点的左子节点为新创建的节点
                        parent.leftChild = newNode;
                        return;
                    }

                }
                //如果插入的值大于当前节点的值,从左子树查找
                else {
                    current = current.rightChild;
                    //直到当前节点为null
                    if(current == null) {
                        //设置当前节点的父节点的右子节点为新创建的节点
                        parent.rightChild = newNode;
                        return;
                    }
                }
            }// end of while(true)
        }   
    }

查找节点

查找节点也是根据其特点进行查找,某个节点的值总比左子树的值大,比右子树的值小或者等于.

public Node find(int value) {

    Node current = root;

    while(current.val != value) {

        if(value < current.val) {
            current = current.leftChild;
        }
        else {
            current = current.rightChild;
        }
        if(current == null) {
            return null;
        }
    }

    return current;
}

删除节点

删除节点比较复杂,因为会出现以下几种情况;
1.删除的节点为叶节点,这个比较好处理,直接将该节点设置为null.
这里写图片描述
2.当删除的是中间节点,其子树如何处理.
2.1 存在左子节点
这里写图片描述
2.2 存在右子节点
这里写图片描述
2.3 存在左右子节点
这里写图片描述

public boolean delete(int value) {

        Node current = root;
        Node parent = root;
        boolean isLeft = false;
        boolean isRight = false;
        //查找所要删除的节点的左子节点还是右子节点
        while(current.val != value) {
            parent = current;
            isLeft = false;
            isRight = false;
            if(value < current.val) {
                current = current.leftChild;
                isLeft = true;
            }
            else {
                current = current.rightChild;
                isRight = true;
            }
        }
        //不存在该值
        if(current == null) {
            return false;
        }
        //是叶子节点,不存在子节点
        if((current.leftChild == null) 
            && (current.rightChild == null)) {
            System.out.println("是叶子节点,不存在子节点");
            if(isLeft) {
                //如果是左子节点,设父节点的左子节点为null
                parent.leftChild = null;
            }
            else if(isRight) {
                //如果是右子节点,设父节点的右子节点为null
                parent.rightChild = null;
            }
            return  true;
        }
        //存在左子节点
        else if((current.leftChild != null) 
                && (current.rightChild == null)) {
                System.out.println("不是叶子节点,存在左子节点");

                if(isLeft) {
                    parent.leftChild = current.leftChild;
                }
                else if(isRight) {
                    parent.rightChild = current.leftChild;
                }
                current = null;
                return  true;
        }

        //存在右子节点
        else if((current.leftChild == null) 
                && (current.rightChild != null)) {
                System.out.println("不是叶子节点,存在右子节点");                
                if(isLeft) {
                    parent.leftChild = current.rightChild; 

                }
                else if(isRight) {
                    parent.rightChild = current.rightChild; 
                }
                current = null;
                return  true;
        }
        //左右子节点都存在
        else {
            System.out.println("不是叶子节点,存在左右子节点");

            if(isLeft) {
                parent.leftChild = current.rightChild; 

                Node currentLeft = current.rightChild;
                Node parentLeft = currentLeft;
                while(currentLeft != null) {
                    parentLeft = currentLeft;
                    currentLeft = currentLeft.leftChild;
                }
                parentLeft.leftChild = current.leftChild;
                current = null;

            }
            else if(isRight) {
                parent.rightChild = current.rightChild; 

                Node currentLeft = current.rightChild;
                Node parentLeft = currentLeft;
                while(currentLeft != null) {
                    parentLeft = currentLeft;
                    currentLeft = currentLeft.leftChild;
                }
                parentLeft.leftChild = current.leftChild;
                current = null;
            }

            return  true;
        }

    }

遍历

前序遍历

public void preOrder(Node localNode) {

    if(localNode != null) {
        System.out.println(localNode.val);
        preOrder(localNode.leftChild);
        preOrder(localNode.rightChild); 
    }   
}

中序遍历

由于中序遍历是根据左(小)-中(中)-右(大),
因此,中序遍历所获得的数据是单调递增的.

public void inOrder(Node localNode) {

    if(localNode != null) {

        inOrder(localNode.leftChild);
        System.out.println(localNode.val);
        inOrder(localNode.rightChild);

    }   
}

后序遍历

public void posOrder(Node localNode) {

    if(localNode != null) {
        posOrder(localNode.leftChild);
        posOrder(localNode.rightChild);         
        System.out.println(localNode.val);
    }   
}

查找最小值

最小值一定位于根节点的左子树中,因此一直从根节点开始一直遍历其左子树,直到该节点没有左子节点.

public Node getMin() {
    Node current = root,last = null;

    while(current != null) {
        last = current;
        current = current.leftChild;
    }

    return last;

}

查找最大值

最小值一定位于根节点的右子树中,因此一直从根节点开始一直遍历其右子树,直到该节点没有右子节点.

public Node getMax() {
    Node current = root,last = null;

    while(current != null) {
        last = current;
        current = current.rightChild;
    }

    return last;        
}

打印二叉搜索树的树形结构

    public  void printTree(Node head) {  
        System.out.println("-----------------\r\nBinary Tree:");  
        printInOrder(head, 0, "Root-", 8);  
        System.out.println();  
    }  

    public  void printInOrder(Node head, int height, String to, int len) {  
        if (head == null) {  
            return;  
        }  
        printInOrder(head.rightChild, height + 1, "R-", len);  
        String val = to + head.val;  
        int lenM = val.length();  
        int lenL = (len - lenM) / 2;  
        int lenR = len - lenM - lenL;  
        val = getSpace(lenL) + val;// + getSpace(lenR);  
        System.out.println(getSpace(height * len) + val);  
        printInOrder(head.leftChild, height + 1, "L-", len);  
    }  

    public  String getSpace(int num) {  
        String space = " ";  
        StringBuffer buf = new StringBuffer("");  
        for (int i = 0; i < num; i++) {  
            buf.append(space);  
        }  
        return buf.toString();  
    } 

存在的问题

二叉搜索树插入时是按照一定规则进行插入的,因此在中序遍历时获得的数据是有序的,比如下面查找35,只需要比较3次就可以获得结果,因此插入或者查找的效率还是比较高的.
这里写图片描述
但是存在的一个问题是,如果插入的数据是单调变化的,那就变成了线性链表,最后导致查找效率降低.
因此,也就出现了其他更好的二叉树数据结构,比如红黑树,红黑树是一种平衡的二叉树,后面琢磨明白了再另写一篇进行说明.

  • 9
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
二叉搜索树是一种常用的据结构,它是一棵二叉树,且每个节点的值都大于其左子树中任何节点的值,而小于其右子树中任何节点的值。Java可以通过实现一个类来定义二叉搜索树。 首先,可以创建一个名为“Node”的内部类来表示二叉树的节点。该类包含三个属性:节点的值,左子树和右子树。TreeNode类的代码如下: ``` class Node { int val; Node left; Node right; public Node(int val) { this.val = val; left = null; right = null; } } ``` 接着,可以定义BinarySearchTree类来表示二叉搜索树。该类包含一个指向根节点的指针,以及实现一些方法来操作二叉搜索树。例如: ``` class BinarySearchTree { Node root; public BinarySearchTree() { root = null; } public void insert(int val) { root = insert(root, val); } private Node insert(Node root, int val) { if (root == null) { root = new Node(val); return root; } if (val < root.val) { root.left = insert(root.left, val); } else if (val > root.val) { root.right = insert(root.right, val); } return root; } public Node search(int val) { return search(root, val); } private Node search(Node root, int val) { if (root == null || root.val == val) { return root; } if (val < root.val) { return search(root.left, val); } else { return search(root.right, val); } } public void delete(int val) { root = delete(root, val); } private Node delete(Node root, int val) { if (root == null) { return root; } if (val < root.val) { root.left = delete(root.left, val); } else if (val > root.val) { root.right = delete(root.right, val); } else { if (root.left == null) { return root.right; } else if (root.right == null) { return root.left; } root.val = minVal(root.right); root.right = delete(root.right, root.val); } return root; } private int minVal(Node root) { int minVal = root.val; while (root.left != null) { minVal = root.left.val; root = root.left; } return minVal; } } ``` 此外,BinarySearchTree类还可以实现其他方法,例如在二叉搜索树中找到最小值、找到最大值、遍历二叉搜索树等。通过这些方法,可以更好地操作二叉搜索树,从而实现各种据存储和查询的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值