Java--二分搜索树

Java–二分搜索树

一、特点
  1. 二分搜索树是一颗二叉树
  2. 二分搜素树的每一个节点的值都大于其左子树所有节点的值,小于右子树所有节点的值在这里插入图片描述
二、底层维护
  • root :树根节点

  • size :树中元素个数

三、注意

树中的节点必须具有可比较性

/**
 * 二分搜索树:
 * 特点:1.每一个节点都比左孩子大,比右孩子小
 *
 * @author a_apple
 * @create 2020-04-20 15:51
 */
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 getSize() {
        return size;
    }

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

  	//...具体操作在下面
}

四、搜索二叉树的基本操作
  1. 向以node为根节点的树中添加元素e,并返回根节点。【递归算法】

注:(除node为null外)元素e最后会和树中一个叶子节点的(左或右)孩子进行比较。叶子节点左右孩子都为空,进入比较后会返回一个的新节点,就是叶子节点的孩子。

	private Node add(Node node, E e) {
        if (node == null) {
            size++;
            return new Node(e);
        }
    	//e<当前节点,进入左子树继续比较。把node当做叶子节点
        if (e.compareTo(node.e) < 0) {
            //插入后node.left 可能会改变,需要重新接受
            node.left = add(node.left, e);
        } else if (e.compareTo(node.e) > 0) {
            //e>当前节点,进入右子树
            node.right = add(node.right, e);
        }
        //不处理相同的情况
        return node;
    }
  1. 查看以node为根节点的二分搜索树是否包含元素e
	private boolean contains(Node node, E e) {
        if (node == null)
            return false;

        if (e.compareTo(node.e) == 0)
            return true;

        if (e.compareTo(node.e) < 0)
            return contains(node.left, e);
        else
            return contains(node.right, e);
    }
  1. 前序遍历
	private void preOrder(Node node) {
        if (node == null) {
            System.out.print("#->");
            return;
        }

        System.out.print(node.e + "->");
        preOrder(node.left);
        preOrder(node.right);
    }
  1. 非递归前序遍历【借助栈

先把根节点压入栈,【1.然后出栈访问。2. 再把左右孩子(非空)压入栈。–>递归进入第一步】

	//借助栈实现前序非递归遍历
    //深度优先遍历
    public void preOrder22() {
        Stack<Node> stack = new Stack<>();
        //1.把根节点压入栈
        stack.push(root);

        while (!stack.isEmpty()) {
            Node pop = stack.pop();
            System.out.print(pop.e + "->");

            //压入左右孩子,因为使用的是栈。所以先压入右孩子,再压左孩子
            //那么左孩子就先出栈
            if (pop.right != null)
                stack.push(pop.right);

            if (pop.left != null)
                stack.push(pop.left);
        }
    }
  1. 后序遍历
	//后序遍历
    private void tailOrder(Node node) {
        if (node == null) {
            System.out.print("#->");
            return;
        }

        tailOrder(node.left);
        tailOrder(node.right);
        System.out.print(node.e + "->");
    }
  1. 中序遍历
	//中序遍历
    private void middleOrder(Node node) {
        if (node == null) {
            System.out.print("#->");
            return;
        }

        middleOrder(node.left);
        System.out.print(node.e + "->");
        middleOrder(node.right);

    }
  1. 层次遍历【借助队列

先把根节点入队,【1.出队访问,2.再把左右孩子入队—>递归->1】

在这里插入图片描述

	//层次遍历,借助队列
    //也叫做广度优先遍历
    public void levelOrder() {
        Queue<Node> queue = new LinkedList<>();
        //1.把根节点压入栈
        queue.add(root);

        //输出这一层(栈内)所有节点
        //同时把这一层的所有节点的子孩子压入栈
        while (!queue.isEmpty()) {
            Node pop = queue.remove();
            System.out.print(pop.e + "->");

            if (pop.left != null)
                queue.add(pop.left);
            if (pop.right != null)
                queue.add(pop.right);
        }
    }
  1. 获取二分搜索树的最大最小值节点
	//最小节点
	//不断往左子树找,最后没有左子树的那个节点,就是最小节点
	private Node miniNode(Node node) {
        if (node.left == null) {
            return node;
        }
        return miniNode(node.left);
    }

	//最大节点:同理
	private Node maxNode(Node node) {
        if (node.right == null)
            return node;
        return maxNode(node.right);
    }
  1. 删除二分搜索树最小值,最大值的节点
  • 删除最小值节点:【不断往左子树寻找
    • node.left = null ,找到最小节点
    • 该节点的右子树不一定为空,所以将右子树第一个节点返回作为新的根节点

在这里插入图片描述

  • 同理:删除最大值节点:【不断往右子树寻找
    • node.right = null , 即为最大值节点
    • 该节点左子树不一定为空,所以将左子树第一个节点返回作为根节点
	private Node removeMin(Node node) {

        // node.left==null 即:如果该节点为最小节点
        // 则返回该节点右子树的第一个节点
        if (node.left == null) {
            //如果左子树为空,则该节点右子树第一个为最大的节点
            Node retNode = node.right;
            //断开与右子树的关系
            node.right = null;
            size--;
            //返回新根节点
            return retNode;
        }
        
        //该节点不是最小节点,继续进入左子树。
        //用node.left 接收删除后的新根节点-->A.left = C
        node.left = removeMin(node.left);
        return node;
    }

 	//返回删除节点后新的二分搜索树的根
    private Node removeMax(Node node) {
 
        // node.right==null    即:当前节点为最大节点
        // 返回左子树第一个节点
        if (node.right == null) {
            //如果右子树为空,则左子树第一个为最大的节点
            Node retNode = node.left;
            //断开与左子树的关系
            node.left = null;
            size--;
            //返回新根节点
            return retNode;
        }

        //当前节点不是最大节点,继续进入右子树寻找
        node.right = removeMax(node.right);
        return node;
    }

  1. 删除以node为根的二分搜索树中值为e的节点,并返回删除后的根节点。【递归算法

“待删除节点”有3种情况:

  • 左子树为空 =>【删除最小节点
  • 右子树为空 =>【删除最大节点
  • 左右子树都不为空【如下图,删除d节点
    • 1 .找到待删除节点 d 的右子树的最小节点 s
    • 2 .用 s 替代 d 的位置
      • s = miniNode(d.right);
      • s.right = removeMini(d.right);
      • s.left = d.left

注:删除节点后依然维持着二分搜索树的性质

在这里插入图片描述

	//删除值为e的节点,返回新的根节点 	
	private Node remove(Node node, E e) {
        if (node == null) {
            return null;
        }

        if (e.compareTo(node.e) < 0) {
            //e<当前节点,进入左子树
            node.left = remove(node.left, e);
            return node;
        } else if (e.compareTo(node.e) > 0) {
            //e>当前节点,进入右子树
            node.right = remove(node.right, e);
            return node;
        } else {
            //找到待删除节点e
            
            //待删除节点左子树为空
            if (node.left == null) {
                Node rightNode = node.right;
                //断开node和右子树的连接
                node.right = null;
                size--;
                //返回删除节点后的(局部)根节点
                return rightNode;
            }

            //待删除节点右子树为空
            if (node.right == null) {
                Node leftNode = node.left;
                //断开node和右子树的连接
                node.left = null;
                size--;
                //返回删除节点后的(局部)根节点
                return leftNode;
            }

            //待删除节点的左右子树均不为空

            //1.寻找“待删除节点”右子树的最小节点
            Node rightMiniNode = miniNode(node.right);
            //2.删除待删节点的右子树的最小值并把新树的作为 “替代节点” 的右子树
            rightMiniNode.right = removeMin(node.right);
            //3.把待删除节点的左子树接到 “替代节点”的左子树
            rightMiniNode.left = node.left;

            //4.将删除的节点断开联系
            node.left = node.right = null;
            return rightMiniNode;
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值