------------二分搜索树-------------
AVL和红黑树都属于平衡二叉树。
二叉树和链表一样,也属于动态数据结构(不需要事先定义容量)
class Node{
E e;
Node left;
Node right;
}
二叉树具有递归结构:每个节点的左子树是二叉树,每个节点的右子树也是二叉树。
满二叉树定义:除了叶子节点,每个节点都有两个孩子。
只有一个节点也可以看成是二叉树,只要满足二叉树的定义
二分搜索树:本身是二叉树,同时必须满足两个条件:1)节点的值大于其左子树的所有节点的值;2)节点的值小于其右子树的所有节点的值。相当于它的每一颗子树也都是二分搜索树。
以上描述的二叉树或者二叉搜索树都不一定是平衡的,即只有左子树或者只有右子树也可能是二分搜索树。
如果二叉树中存放自定义数据,要定义好两个自定义数据之间是如何进行大小比较的,这样二分树中才能查找数据。
1) 二分搜索树中添加元素
2) 二分搜索树中搜索元素-前序遍历、中序遍历和后序遍历(均是递归调用)(前中后序遍历都属于深度优先遍历)
3) 使用栈实现非递归调用的前序遍历
4) 层序遍历(层序遍历属于广度优先遍历):不是用递归,是借助队列实现,广度优先遍历的意义是更快的找到问题的解
5) 二分搜索树删除节点
具体代码如下:
package tree;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
/*
* 这是定义的二分搜索树,树中存放的数据必须明确声明如何比较大小
* 树中存放的数据E必须继承Comparable接口,这样才能保证E具有可比性
* 如下实现的二分搜索树是不包含重复元素的,也可尝试自行实现可包含重复元素的二分搜索树
* */
public class BST<E extends Comparable<E>> {
    //定义匿名内部类
    private class Node{
        public E e;
        public Node left;
        public Node right;
        public Node(E e){
            this.e = e;
            left = null;
            right 
= null;
        }
    }
    //定义两个成员变量root和size
    private Node root;
    private int size;
    //定义二分搜索树的构造方法
    public BST(){
        root = null;
        size = 0;
    }
    public int getSize(){
        return size;
    }
    public boolean isEmpty(){
        return size == 0;
    }
    /*//向二分搜索树中增加新的元素e
    public void add(E e){
        if(root == null){
            root = new Node(e);
            size++;
        }else{
            add(root,e);
        }
    }
    //如果e小于node中的e,递归左子树,如果e大于node中的e,递归右子树,直到比较的node是根节点,不需要断开两个节点直接的链接
    private void add(Node node,E e){
        //终止条件
        if(e.compareTo(node.e) == 0){
            //如果相等,不做处理,类的头部已经说明本二叉搜索树不包含重复数据
            return;
        }else if(e.compareTo(node.e) <
0 && node.left == null){
            //如果小于node.e并且node的left节点已经是null
            node.left = new Node(e);
            size++;
            return;
        }else if(e.compareTo(node.e)>0
&& node.right == null){
            node.right = new Node(e);
            size++;
            return;
        }
        //递归调用
        if(e.compareTo(node.e) < 0){
            //如果小于node.e并且node的left节点不为null
            add(node.left,e);
            //最终终止条件一会被执行到,终止条件中进行了size++,所以此处不要再进行size++,否则就加多了
        }else{
            add(node.right,e);
        }
    }*/
    //优化后的,向二分搜索中加新的元素饿
    public void add(E e){
        root = add(root,e);
    }
    //优化后的,返回插入新节点后二分搜索树的根
    private Node add(Node node,E e){
        //如果首次调用,相当于创建根节点
        //如果不是首次调用,相当于是创建叶子节点,拼接在父节点下
        //终结条件
        if(node == null){
            size++;
            return new Node(e);
        }
        //如果传进来的node不为null,则将e添加到node的左子树中
        if(e.compareTo(node.e) < 0){
            //如果node.left为null,相当于将e新new的node做为node.left
            //如果node。left不为null,相当于将e新new的node递归加入node。left对应的左子树中
            //注意,左节点为null是做为左节点,不为null,是加入到左子树中,是左节点和左子树的区别
            //最后一次调用add(node.left,e),一定是node.left为null,这样才是终结条件
            node.left 
= add(node.left,e);
        }else if(e.compareTo(node.e) > 0){
            node.right = add(node.right,e);
        }
        //最终返回node
        return node;
    }
    //前序遍历(先遍历根节点),用户使用
    public void preOrder(){
        preOrder(root);
    }
    //私有的前序遍历,递归使用
    private void preOrder(Node node){
        //终止条件
        //整个链表为null,或者子节点为null
        if(node == null){
            return;
        }
        //输出当前节点
        System.out.println(node.e);
        //递归调用
        //遍历左子树右子树
        preOrder(node.left);
        preOrder(node.right);
    }
    @Override
    //前序遍历生成toString()的字符串
    public String toString() {
        StringBuilder sb = new StringBuilder();
        generateBSTString(root,0,sb);
        return sb.toString();
    }
    //前序遍历,先访问该节点,再遍历左子树,再遍历右子树
    private void generateBSTString(Node node,int depth,StringBuilder sb){
        //终止条件
        if(node == null){
            return;
        }
        //中间这部分是逻辑处理
        for(int i= 0;i<depth;i++){
            sb.append("--");
        }
        sb.append(node.e);
        sb.append("\n");
        //递归调用
        generateBSTString(node.left,depth+1,sb);
        generateBSTString(node.right,depth+1,sb);
    }
    //中序遍历:先遍历左子树,再遍历该节点,再遍历右子树
    //中序遍历的输出结果是从小到的
    public void middleOrder(){
        middleOrder(root);
    }
    //私有的中序遍历,递归使用
    private void middleOrder(Node node){
        //终止条件
        //整个链表为null,或者子节点为null
        if(node == null){
            return;
        }
        //递归调用
        //遍历左子树
        middleOrder(node.left);
        //输出当前节点
        System.out.println(node.e);
        //遍历右子树
        middleOrder(node.right);
    }
    //后续遍历:先访问左子树、再访问右子树、最后访问当前节点
    //后续遍历的典型应用是为二分搜索树释放内存,因为必须处理完左右子节点,才能处理当前节点
    //又或者是分支算法,都是因为需要先处理完两个子节点,再根据子节点的结果来处理当前节点
    public void postOrder(){
        postOrder(root);
    }
    //私有的中序遍历,递归使用
    private void postOrder(Node node){
        //终止条件
        //整个链表为null,或者子节点为null
        if(node == null){
            return;
        }
        //递归调用
        //遍历左子树
        postOrder(node.left);
        //遍历右子树
        postOrder(node.right);
        //输出当前节点
        System.out.println(node.e);
    }
    //使用栈以非递归的形式前序遍历
    public void preOrderUserStack(){
        Stack<Node> stack = new Stack();
        stack.push(root);
        while(!stack.isEmpty()){
            Node node = stack.pop();
            System.out.println(node.e);
            //由于用的是栈,需要右子树先入栈,左子树后入栈,这样才能左子树先出栈
            if(node.right != null){
                stack.push(node.right);
            }
            if(node.left != null){
                stack.push(node.left);
            }
       
}
    }
    //层序遍历,借助队列:先进先出
    public void levelOrder(){
        Queue<Node> queue = new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()){
            Node current = queue.poll();
            System.out.println(current.e);
            if(current.left != null){
                queue.add(current.left);
            }
            if(current.right != null){
                queue.add(current.right);
            }
        }
    }
    //寻找二分搜索树中的最小元素
    public E minimum(){
        if(size == 0){
            throw 
new IllegalArgumentException("BST is empty.");
        }
        //最小元素
        Node minumum = minumum(root);
        return minumum.e;
    }
    //私有的内部递归调用的的方法
    private Node minumum(Node node){
        //终止条件
        if(node.left == null){
            return  node;
        }
        return minumum(node.left);
    }
    //寻找二分搜索树中的最大元素
    public E maximum(){
        if(size == 0){
            throw 
new IllegalArgumentException("BST is empty.");
        }
        //最小元素
        Node maximum = maximum(root);
        return maximum.e;
    }
    //私有的内部递归调用的的方法
    private Node maximum(Node node){
        //终止条件
        if(node.right == null){
            return  node;
        }
        return maximum(node.right);
    }
    //删除二分搜索树中的最小元素
    public E removeMin(){
        E min = minimum();
        //删除最大值后,将该二分搜索树重新赋值给root
        root = removeMin(root);
        return min;
    }
    //返回删除最小节点后的node
    public Node removeMin(Node node){
        //终止条件
        if(node.left == null){
            Node rightNode = node.right;
            node.right = null;
            size--;
            return rightNode;
        }
        //相当于如果node.left是最小的节点,
        //使node.left = node.left.right
        node.left = removeMin(node.left);
        return node;
    }
    //删除最大根节点
    public E removeMax(){
        E e = maximum();
        root = removeMax(root);
        return e;
    }
    //删除最大元素
    public Node removeMax(Node node){
        //终止条件
        if(node.right == null){
            Node leftNode = node.left;
            node.left = null;
            size--;
            return leftNode;
        }
        //如果node.right是最大值,则删除该最大值
        //并使node.right重新指向node.right.left
        node.right = removeMax(node.right);
        return node;
    }
    /*
    * 删除二分搜索树中的任意元素
    * 1、终止条件:e等于node.e,
    *       如果node.left==null,返回node.right;
    *       如果node.right==null,返回node.left;
    *       如果都不为空,则返回右子树中最小节点做为根的新的二分搜索树;
    * 2、如果不满足终止条件,则需要递归调用
    *  
如果e<node.e,则递归调用的返回值做为新的node.left;
    *  
如果e>node.e,则递归调用的返回值做为新的node.right;
    * 
* */
    public void removeElement(E e){
        root = removeElement(root,e);
    }
    private Node removeElement(Node node,E e){
        //1终止条件
        if(e.compareTo(node.e) == 0){
            if(node.left == null){
                size--;
                return node.right;
            }else if(node.right == null){
                size--;
                return node.left;
            }else{
                Node rightNode = node.right;
                //获取右子树的最小值
                Node min = 
minumum(rightNode);
                //删除右子树的最小值,并返回最新的右子树
                Node newRightNode = removeMin(rightNode);
                //以min为根,创建新的二分搜索树
                min.left = node.left;
                min.right = newRightNode;
                //此步不需要size--,因为removeMin的时候已经进行了一次size--
                //size--;
                return min;
            }
            /*//如果右子树不为null
            if(node.right != null){
                Node rightNode =
node.right;
                //获取右子树的最小值
                Node min =  minumum(rightNode);
                //删除右子树的最小值,并返回最新的右子树
                Node newRightNode =
removeMin(rightNode);
                //以min为根,创建新的二分搜索树
                min.left = node.left;
                min.right = newRightNode;
                size--;
                return min;
            }else{
                //如果右子树为null,则返回左子树
                size--;
                return node.left;
            }*/
        }
        //2如果不满足终止条件
        if(e.compareTo(node.e) < 0){
            //如果e<node.e,则node.left指向递归调用新城的新的二分搜索树
            if(node.left != null){
                node.left = removeElement(node.left,e);
            }
        }else{
            //如果e>node.e,则node.right指向递归调用新城的新的二分搜索树
            if(node.right != null){
                node.right = removeElement(node.right,e);
            }
        }
        return node;
    }
}
测试代码如下:
package tree;
import java.util.ArrayList;
import java.util.Random;
public class Main {
    public static void main(String[] args) {
        BST<Integer> bst = new BST<>();
        int[] arr = {5,3,6,8,4,2};
        for(int i=0;i<arr.length;i++){
            bst.add(arr[i]);
        }
        //前序遍历
        bst.preOrder();
        //前序遍历的方式写的toString()
        System.out.println(bst);
        //中序遍历,从小到达输出
        bst.middleOrder();
        //后续遍历
        bst.postOrder();
        //非递归的前序遍历
        System.out.println("非递归的前序遍:");
        bst.preOrderUserStack();
        //层序遍历,使用队列实现
        System.out.println("层序遍历:");
        bst.levelOrder();
        //测试removeMin()方法是否正确
        testRemoveMin();
        //测试删除指定元素
        System.out.println("测试删除指定元素:");
        bst.removeElement(2);
        bst.levelOrder();
        //System.out.println(bst.getSize());
    }
    public static void testRemoveMin(){
        Random random = new Random();
        BST<Integer> bst = new BST<>();
        for(int i=0;i<1000;i++){
            bst.add(random.nextInt(10000));
        }
        ArrayList<Integer> arr = new ArrayList<>();
        while(!bst.isEmpty()){
            arr.add(bst.removeMin());
        }
        System.out.println(arr);
        for(int i=0;i<arr.size()-1;i++){
            if(arr.get(i) > arr.get(i+1)){
                throw new IllegalArgumentException("Error");
            }
        }
        System.out.println("removeMin is right.");
    }
}
小总结:栈可用在前序遍历(深度优先遍历),队列可用在层序遍历(广度优先遍历)
如有理解不到之处,望指正!
原文:https://www.cnblogs.com/xiao1572662/p/12106874.html