【数据结构】二分搜索树小结

一、基本概念

二分搜索树:(又:二分查找树,二叉排序树)它或者是一棵空树,或者是具有下列性质:

                     若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;

                     若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;

                     它的左、右子树也分别为二叉排序树

二、代码实现

1.首先构建二分搜索数的基本结构

  这里使用泛型来构建,始终记住二分搜索树存储的元素必须有可比较性

  size用来记录二分搜索的元素个数

public class BST<E extends Comparable<E>> {

    private class Node {
        public E e;
        public Node left, right;

        public Node(E e) {
            this.e = e;
            left = null;
            right = null;
        }
    }

    private Node root;
    private int size;

    public BST() {
        root = null;
        size = 0;
    }

    public int size() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }
}

2.实现二分搜索树的添加方法

 //向二分搜索树添加新的元素e
    public void add(E e) {
        root = add(root, e);
    }

    //向以node为根的二分搜索树中插入元素e,递归
    //返回插入新节点后的二分搜索树
    private Node add(Node node, E e) {
        if (node == null) {
            size++;
            return new Node(e);
        }

        if (e.compareTo(node.e) < 0) {
            node.left = add(node.left, e);
        } else if (e.compareTo(node.e) > 0) {
            node.right = add(node.right, e);
        }
        //向以node为根的二分搜索树中插入元素e,但二分搜索树的根还是node,所以返回根节点
        return node;
    }

3.实现二叉搜索树的查询方法

//判断是否包含元素e
    public boolean contains(E e) {
        return contain(root, e);
    }

    private boolean contain(Node node, E e) {
        if (node == null)
            return false;
        if (e.compareTo(node.e) == 0) {
            return true;
        } else if (e.compareTo(node.e) < 0) {
            return contain(node.left, e);
        } else{
            return contain(node.right, e);
        }
    }

4.实现二叉树的前、中、后序遍历

   1.前序遍历

//前序遍历
    public void preOrder() {
        preOrder(root);
    }

    //谦虚遍历以node为根的二分搜索树,递归算法
    private void preOrder(Node node) {
        if (node == null)
            return;
        System.out.print(node.e);
        preOrder(node.left);
        preOrder(node.right);
    }

  2.中序遍历

//中序遍历
    public void inOrder() {
        inOrder(root);
    }

    //中序结果为从小到大
    private void inOrder(Node root) {
        if (root == null)
            return;
        inOrder(root.left);
        System.out.print(root.e);
        inOrder(root.right);
    }

  3.后序遍历

//后序遍历
    public void postOrder() {
        postOrder(root);
    }

    private void postOrder(Node root) {
        if (root == null)
            return;
        postOrder(root.left);
        postOrder(root.right);
        System.out.print(root.e);
    }

  4.测试前、中、后遍历

     这里先是构建了一个名为bst的二分搜索树,然后分别进行前、中、后序输出

     有两个System.out.println是为了将前、中、后序分行来输出

public class Main {
    public static void main(String[] args) {
        BST<Integer> bst = new BST<>();
        int[] nums = {5,3,6,8,4,2,1};
        for(int num:nums)
            bst.add(num);
        bst.preOrder();
        System.out.println();
        bst.inOrder();
        System.out.println();
        bst.postOrder();


    }
}

  构建的二分搜索树如下(每次添加元素都会先从root根结点开始向下比较

    

    测试结果:

    前序:5321468
    中序:1234568            注意!!!   可以发现二分搜索树的中序遍历结果是从小到大输出元素的
    后续:1243865

5.二叉搜索树的层序遍历

  层序遍历顾名思义就是一层一层的遍历,也称为广度优先遍历

  这里借助队列实现层序遍历,首先将root根节点添加到队列中,然后移除root再将其左右子树添加.....

public void levelOrder(){
        Queue<Node> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()){
            Node cur = queue.remove();
            System.out.println(cur.e);
            if(cur.left != null)
                queue.add(cur.left);
            if(cur.right != null)
                queue.add(cur.right);
        }
    }

6.删除二叉树的最小、最大值结点

        删除二叉树最小值结点思路:一遍递归寻找node,left==null; 的结点,该结点就是该二叉搜索树的最小值结点,此时需要删除该结点,只需将结点被赋值为该结点的右子树即可。

//从二分搜索树中删除小值所在结点,返回最小值
    public E removeMin(){
        //miniNode()函数返回该树的最小值
        E min = miniNode();
        //removeMin()函数删除该树的最小值且返回删除后最小值的根结点
        root = removeMin(root);
        return min;
    }

    private Node removeMin(Node node) {
        //当node结点的左孩子为空时,可以判断出此时node为此二叉搜索树的最小值,所以删除该结点
        //使用该node结点的右子树代替node
        if(node.left==null){
            Node rightNode = node.right;
            node.right = null;
            size--;
            return rightNode;
        }
        node.left = removeMin(node.left);
        return node;
    }

        删除二叉树最大值结点思路:一遍递归寻找node,right==null; 的结点,该结点就是该二叉搜索树的最大值结点,此时需要删除该结点,只需将结点被赋值为该结点的左子树即可。
 


    public E removeMax(){
        E max = maxNode();
        root = removeMax(root);
        return max;
    }

    private Node removeMax(Node node) {
        if(node.right==null){
            Node leftNode = node.left;
            node.left = null;
            size--;
            return leftNode;
        }
        node.right = removeMax(node.right);
        return node;
    }

      注意:删除二叉搜索树只包含左孩子和只包含右孩子其实和删除最小、最大值方法类似。

 

 7.删除二叉搜索树的任意结点

       对于上面的第6点,只是删除最小或最大值的节点,最小和最大值节点对于二叉搜索树而言绝对是叶子节点。

       然而对于删除二叉搜索树的任意节点,此时就要考虑到如果待删节点左右孩子都不为空的情况。

       对于下面的二叉搜索树如果要删除的节点是58,那么删除之后应该是用那个节点来代替58节点的位置呢?

       仔细观察下图会发现可以选择58节点右子树后节点的最小值!也就是59号节点。所以也就是找到待删节点右子树后最小值,将该最小值去替代待删节点  (前提是,该待删节点左右孩子都不为空,如果为空就是上面第6点讲的方法了~)

//删除以node为根的二分搜索树中值为e的结点
    private Node remove(Node node, E e) {
        if(node == null)
            return null;
        if(e.compareTo(node.e) < 0){
            node.left = remove(node.left,e);
            return node;
        }else if(e.compareTo(node.e) > 0){
            node.right = remove(node.right,e);
            return node;
        } else{
            //如果该待删节点左子树为空
            if(node.left == null){
                Node rightNode = node.right;
                node.right = null;
                size--;
                return rightNode;
            }
            //如果该待删节点右子树为空
            if(node.left == null){
                Node leftNode = node.right;
                node.right = null;
                size--;
                return leftNode;
            }
            //待删除节点左右子树都不为空
            //调用miniNode函数找出node右子树后最小的节点值
            Node successor = miniNode(node.right);
            //调用removeMin函数删除node右子树后最小值的节点
            successor.right = removeMin(node.right);
            successor.left = node.left;
            
            node.left = node.right = null;
            return successor;
        }
    }

     如果将上面代码看懂了后,你也许会想到对于删除有左右孩子的节点,也可以采用待删节点的左孩子的最大值来代替自己!

     如下图待删节点58,你也可以选择用该待删节点的左子树中的最大值,就是53来代替自己

 

猜你喜欢

转载自blog.csdn.net/weixin_41963657/article/details/89069982
今日推荐