数据结构-二叉搜索树

二叉搜索树是一种特殊类型的二叉树,它的特点是在子节点的值<根节点值<游子节点的值,利用二叉搜索树进行查询的效率为O(logn),与折半查找相当,在一些数据检索相关的程序中很多都用到了二叉树或二叉树的优化与变种树,对于一个二叉树的操作主要包括以下几个:

  • 初始化
  • 新增节点
  • 判断是否包含某个值
  • 查找最小值
  • 查询最大值
  • 删除最小值
  • 删除最大值
  • 删除给定的值
  • 前序遍历
  • 中序遍历
  • 后续遍历

下面试一个二叉搜索树的具体实现:

package com.base.ds.csdn.tree;

import java.util.LinkedList;
import java.util.Queue;

/**
 * 二分搜索树.
 * 特点:左子节点 < 根节点 < 右子节点.
 * 操作:
 * 1. 添加
 * 2. 删除
 * 3. 遍历(前、中、后序遍历、层序遍历)
 * */
public class BSTree<E extends Comparable<E>> {

    private Node<E> root;
    private int size;

    //初始化
    public BSTree() {
        this.root = null;
        size = 0;
    }

    /**
     * 添加数据.
     * */
    public void add(E e) {
        //添加操作完成后,root节点可能发生变化
        root = add(e, root);
    }

    //利用递归,添加节点
    private Node<E> add(E e, Node<E> node) {
        if (node == null) {
            size ++;
            return new Node(e);
        }

        //如果当前数据比当前节点的值小,则向左添加
        if(e.compareTo(node.data) < 0) {
            node.left = add(e, node.left);
        } else if (e.compareTo(node.data) > 0) {
            node.right = add(e, node.right);
        }
        return node;
    }

    /**
     * 判断释放包含某个数据
     * */
    public boolean contains(E e) {
        if (isEmpty()) {
            throw new IllegalArgumentException("bst is empty.");
        }
        return contains(e, root);
    }

    //利用递归来判断释放包含
    private boolean contains(E e, Node<E> node) {
        if (node == null) {
            return false;
        }

        if (e.compareTo(node.data) == 0) {
            return true;
        } else if (e.compareTo(node.data) < 0) {
            return contains(e, node.left);
        } else {
            return contains(e, node.right);
        }
    }

    /**
     * 找到最小的数据
     * */
    public E min() {
        if (isEmpty()) {
            throw new IllegalArgumentException("bst is empty.");
        }
        return minNode(root).data;
    }

    private Node<E> minNode(Node<E> node) {
        if (node.left == null) {
            return node;
        }
        return minNode(node.left);
    }

    /**
     * 找到最大的数据
     * */
    public E max() {
        if (isEmpty()) {
            throw new IllegalArgumentException("bst is empty.");
        }
        return maxNode(root).data;
    }
    private Node<E> maxNode(Node<E> node) {
        if (node.right == null) {
            return node;
        }
        return maxNode(node.right);
    }

    /**
     * 删除最小值
     * */
    public E removeMin() {
        E ret = min();
        root = removeMin(root);
        return ret;
    }

    //用递归的方式实现
    private Node<E> removeMin(Node<E> node) {
        if (node.left == null) {
            Node<E> right = node.right;
            node.right = null;
            size --;
            return right;
        }
        node.left = removeMin(node.left);
        return node;
    }

    /**
     * 删除最大值
     * */
    public E removeMax() {
        E ret = max();
        root = removeMax(root);
        return ret;
    }

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

    /**
     * 移除给定值的节点
     * */
    public void remove(E e){
        root = remove(e, root);
    }

    private Node<E> remove(E e, Node<E> node) {
        //如果没有找到目标节点就直接范围null
        if (node == null) {
            return null;
        }

        //给定值比当前节点的值小
        if (e.compareTo(node.data) < 0) {
            node.left = remove(e, node.left);
            return node;
        } else if (e.compareTo(node.data) > 0) {
            //给定值比当前节点的值大
            node.right = remove(e, node.right);
            return node;
        } else {
            //找到了要删除的节点
            /**
             * 没有左子节点
             * 没有右子节点
             * 即有左子节点也有右子节点
             * */

            //没有左子树
            if (node.left == null) {
                Node<E> right = node.right;
                node.right = null;
                size --;
                return right;
            }

            //没有右子树
            if (node.right == null) {
                Node<E> left = node.left;
                node.left = null;
                size --;
                return left;
            }

            //即有左子树又有右子树
            //思路是:用左子树中的最大节点 或 用右子树中的最小节点来替换删除的节点,这样可以
            //保证二叉搜索树的性质

            //找到又子树中的最小节点
            Node<E> rightMinNode = minNode(node.right);
            //将右子树中的最小节点删除,并把删除后的新的根节点挂在 上面得到的Node的右侧分支
            rightMinNode.right = removeMin(node.right);
            //将当前待删除节点的left挂到rightMinNode上
            rightMinNode.left = node.left;

            //help GC
            node.right = null;
            node.left = null;
            return rightMinNode;
        }
    }

    /**
     * 前序遍历:父节点、左节点、右节点
     * 中序遍历:左节点、父节点、右节点
     * 后续遍历:左节点、右节点、父节点
     * 层序遍历:一层一层遍历
     * */
    //前
    public void preOrder() {
        preOrder(root);
    }

    private void preOrder(Node<E> node) {
        if (node == null) {
            return;
        }
        System.out.println(node.data);
        preOrder(node.left);
        preOrder(node.right);
    }

    //中
    public void inOrder() {
        inOrder(root);
    }

    private void inOrder(Node<E> node) {
        if (node == null) {
            return;
        }
        inOrder(node.left);
        System.out.println(node.data);
        inOrder(node.right);
    }

    //后
    public void postOrder() {
        postOrder(root);
    }

    private void postOrder(Node<E> node) {
        if (node == null) {
            return;
        }
        postOrder(node.left);
        postOrder(node.right);
        System.out.println(node.data);
    }

    //层序遍历,借助队列
    public void levelOrder() {
        if (root == null) {
            return;
        }
        Queue<Node<E>> queue = new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty() && root != null) {
            Node<E> cur = queue.remove();
            System.out.println(cur.data);
            if (cur.left != null) {
                queue.add(cur.left);
            }
            if (cur.right != null) {
                queue.add(cur.right);
            }
        }
    }



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

    private static class Node<E extends Comparable<E>>{
        private E data;
        private Node<E> left;
        private Node<E> right;
        public Node(E elem) {
            this(elem, null, null);
        }

        public Node(E elem, Node<E> left, Node<E> right) {
            this.left = left;
            this.right = right;
            this.data = elem;
        }
        @Override
        public String toString() {
            return "Node{" +
                    "data=" + data +
                    '}';
        }
    }

    public static void main(String[] args) {
        BSTree<Integer> bsTree = new BSTree<>();
        bsTree.add(5);
        bsTree.add(3);
        bsTree.add(8);
        bsTree.add(2);
        bsTree.add(4);
        bsTree.add(1);
        bsTree.add(7);
        bsTree.add(10);
        bsTree.add(9);
        bsTree.add(15);

        /**

                5
              3      8
           2    4  7   10
         1            9   15

         1,2,3,4,5,7.,8,9,10,15
         * */
        bsTree.postOrder();
        bsTree.levelOrder();
    }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

echo20222022

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值