Binary Search Tree - HolmesJJ/Data-Structures-and-Algorithms GitHub Wiki

入门链接

二叉排序树

图解

binary_search_tree

详解

时间复杂度

先序遍历 中序遍历 后序遍历 层次遍历 height
时间复杂度 O(n) O(n) O(n) O(n) O(h)
findMin findMax insert delete findNode predecessor successor
时间复杂度 O(h) O(h) O(h) O(h) O(h) O(h) O(h)

若树严重左偏或右偏,h会变成n

标准写法

步骤1: 定义Tree的接口,即Tree应该有的功能
步骤2: 定义Binary Tree的结点Note
二叉树
步骤3: 完成BinarySearchTree

// Comparable是为了比较两个结点之间的值(遍历操作,插入删除操作),具体的比较方法需要在值的类型确定后才能确定
public class BinarySearchTree<T extends Comparable<T>> implements Tree<T> {

    private BinaryNode<T> root;

    public BinaryNode<T> getRoot() {
        return root;
    }

    public void setRoot(BinaryNode<T> root) {
        this.root = root;
    }

    // 初始化一个空的二叉搜索树
    public BinarySearchTree() {
        this.root = null;
    }

    // 初始化一个根结点
    public BinarySearchTree(BinaryNode<T> root) {
        this.root = root;
    }

    // 根据先序遍历和中序遍历,或中序遍历和后序遍历构造二叉树
    // list可以是先序遍历或后序遍历
    public BinarySearchTree(T[] list, T[] inOrderList, boolean isPreOrder) {
        if (list == null|| inOrderList == null) {
            throw new RuntimeException("lists cannot be null");
        }
        if (isPreOrder) {
            // 先序遍历和中序遍历构建二叉树
            this.root = createBinarySearchTreeByPreIn(list, inOrderList, 0, list.length - 1, 0, inOrderList.length - 1);
        }else {
            // 中序遍历和后序遍历构建二叉树
            this.root = createBinarySearchTreeByPostIn(list, inOrderList, 0, list.length - 1, 0, inOrderList.length - 1);
        }
    }

    /**
     * 根据先序遍历和中序遍历构造二叉树
     * @param preOrderList 先序遍历数组
     * @param inOrderList 中序遍历数组
     * @param preOrderStart 先序遍历数组的头结点位置
     * @param preOrderEnd 先序遍历数组的尾结点的位置
     * @param inOrderStart 中序遍历数组的头结点位置
     * @param inOrderEnd 中序遍历数组的尾结点的位置
     * return root 最终返回的根结点
     */
    public BinaryNode<T> createBinarySearchTreeByPreIn(
            T[] preOrderList, T[] inOrderList, int preOrderStart ,int preOrderEnd ,int inOrderStart ,int inOrderEnd) {

        // 先序遍历第一个点是根结点root
        BinaryNode<T> p = new BinaryNode<>(preOrderList[preOrderStart]);
        // 如果没有其它元素,就说明结点已构建完成
        if (preOrderStart == preOrderEnd && inOrderStart == inOrderEnd) {
            return p;
        }

        // 找出中序遍历的根结点下标rootIndex
        int rootIndex = 0;
        for (rootIndex = inOrderStart; rootIndex < inOrderEnd; rootIndex++) {
            // preOrderList[preOrderStart]是先序遍历第一个点,即是根结点root
            // 如果中序遍历中的元素值与先序遍历的根结点相等,则该下标index即为inOrderList中的根结点下标
            if (preOrderList[preOrderStart].compareTo(inOrderList[rootIndex]) == 0){
                break;
            }
        }

        // 获取左子树的长度
        int leftLength = rootIndex - inOrderStart;
        // 获取右子树的长度
        int rightLength = inOrderEnd - rootIndex;
        // 递归构建左子树
        if (leftLength > 0) {
            // 左子树的先根序列:preOrderList[1], ... , preOrderList[i]
            // 左子树的中根序列:inOrderList[0], ... , inOrderList[i-1]
            p.setLeft(createBinarySearchTreeByPreIn(
                    preOrderList, inOrderList, preOrderStart + 1,preOrderStart + leftLength, inOrderStart, rootIndex - 1));
        }
        // 递归构建右子树
        if (rightLength > 0) {
            // 右子树的先根序列:preOrderList[i+1], ... , preOrderList[n-1]
            // 右子树的中根序列:inOrderList[i+1], ... , inOrderList[n-1]
            p.setRight(createBinarySearchTreeByPreIn(
                    preOrderList, inOrderList,preOrderStart + leftLength + 1, preOrderEnd,rootIndex + 1, inOrderEnd));
        }
        return p;
    }

    /**
     * 根据中序遍历和后序遍历构造二叉树
     * @param postOrderList 后序遍历数组
     * @param inOrderList 中序遍历数组
     * @param postOrderStart 后序遍历数组的头结点位置
     * @param postOrderEnd 后序遍历数组的尾结点的位置
     * @param inOrderStart 中序遍历数组的头结点位置
     * @param inOrderEnd 中序遍历数组的尾结点的位置
     * @return 最终返回的根结点
     */
    public BinaryNode<T> createBinarySearchTreeByPostIn(
            T[] postOrderList, T[] inOrderList, int postOrderStart, int postOrderEnd, int inOrderStart, int inOrderEnd) {

        // 后序遍历最后一个点是根结点root
        BinaryNode<T> p = new BinaryNode<>(postOrderList[postOrderEnd]);
        // 如果没有其它元素,就说明结点已构建完成
        if (postOrderStart == postOrderEnd && inOrderStart == inOrderEnd) {
            return p;
        }

        // 找出中序遍历的根结点下标rootIndex
        int rootIndex = 0;
        for (rootIndex = inOrderStart; rootIndex < inOrderEnd; rootIndex++) {
            // postOrderList[postOrderEnd]是后序遍历最后一个点,即是根结点root
            // 如果中序遍历中的元素值与后序遍历的根结点相等,则该下标index即为inOrderList中的根结点下标
            if (postOrderList[postOrderEnd].compareTo(inOrderList[rootIndex]) == 0){
                break;
            }
        }

        // 获取左子树的长度
        int leftLength = rootIndex - inOrderStart;
        // 获取右子树的长度
        int rightLength = inOrderEnd - rootIndex;
        // 递归构建左子树
        if (leftLength > 0) {
            // 左子树的后根序列:postOrderList[0], ... , postOrderList[i-1]
            // 左子树的中根序列:inOrderList[0], ... , inOrderList[i-1]
            p.setLeft(createBinarySearchTreeByPostIn(
                    postOrderList, inOrderList, postOrderStart, postOrderStart + leftLength - 1, inOrderStart,rootIndex - 1));
        }
        // 递归构建右子树
        if (rightLength > 0) {
            // 右子树的后根序列:preOrderList[i], ... , preOrderList[n-2]
            // 右子树的中根序列:inOrderList[i+1], ... , inOrderList[n-1]
            p.setRight(createBinarySearchTreeByPostIn(
                    postOrderList, inOrderList, postOrderStart + leftLength, postOrderEnd - 1,rootIndex + 1, inOrderEnd));
        }
        return p;
    }

    // 判断二叉树搜索是否为空
    // 只需要判断根结点是否存在即可
    @Override
    public boolean isEmpty() {
        return root == null;
    }

    // 思路:获取二叉树节点数,需要获取以某个节点为根的子树的节点数实现。
    // 如果节点为空,则个数肯定为0;如果不为空,则算上这个节点之后,继续递归计算所有子树的节点数,全部相加即可
    @Override
    public int size() {
        return size(root);
    }

    private int size(BinaryNode<T> subtree) {
        if (subtree == null) {
            // 如果结点为空,则返回节点数为0
            return 0;
        } else {
            // 计算本节点 所以要 +1
            // 递归获取左子树节点数和右子树节点数,最终相加
            // 对比汉诺塔:H(n) = H(n-1) + 1 + H(n-1)
            return size(subtree.getLeft()) + size(subtree.getRight()) + 1;
        }
    }

    // 思路:首先需要一种获取以某个节点为子树的高度方法,使用递归实现。
    // 如果一个节点为空,那么这个节点肯定是一颗空树,高度为0;
    // 如果不为空,则遍历地比较它的左右子树高度,高的一个为这颗子树的最大高度,然后加上自身的高度即可
    private int height(BinaryNode<T> subtree) {
        if (subtree == null) {
            // 递归结束,空子树高度为0
            return 0;
        } else {
            // 递归获取左子树高度
            int l = height(subtree.getLeft());
            // 递归获取右子树高度
            int r = height(subtree.getRight());
            // 高度应该算更高的一边,+1是因为药算上自身这一层
            return l > r ? (l + 1) : (r + 1);
        }
    }

    @Override
    public int height() {
        return height(root);
    }

    // 若二叉树为空,则退出,否则进行下面操作
    // 访问根结点
    // 先根遍历左子树
    // 先根遍历右子树
    // 退出
    public String preOrder(BinaryNode<T> subtree) {
        StringBuilder sb = new StringBuilder();
        // 递归结束条件
        if (subtree != null) {
            // 访问根结点
            sb.append(subtree.getData()).append(",");
            // 遍历左子树
            sb.append(preOrder(subtree.getLeft()));
            // 遍历右子树
            sb.append(preOrder(subtree.getRight()));
        }
        return sb.toString();
    }

    @Override
    public String preOrder() {
        String sb = preOrder(root);
        if (sb.length() > 0) {
            // 去掉尾部","号
            sb = sb.substring(0, sb.length() - 1);
        }
        return sb;
    }

    // 若二叉树为空,则退出,否则进行下面操作
    // 中根遍历左子树
    // 访问根节点
    // 中根遍历右子树
    // 退出
    public String inOrder(BinaryNode<T> subtree) {
        StringBuilder sb = new StringBuilder();
        // 递归结束条件
        if (subtree != null) {
            // 遍历左子树
            sb.append(inOrder(subtree.getLeft()));
            // 访问根结点
            sb.append(subtree.getData()).append(",");
            // 遍历右子树
            sb.append(inOrder(subtree.getRight()));
        }
        return sb.toString();
    }

    @Override
    public String inOrder() {
        String sb = inOrder(root);
        if (sb.length() > 0) {
            // 去掉尾部","号
            sb = sb.substring(0, sb.length() - 1);
        }
        return sb;
    }

    // 若二叉树为空,则退出,否则进行下面操作
    // 后根遍历左子树
    // 后根遍历右子树
    // 访问根节点
    // 退出
    public String postOrder(BinaryNode<T> subtree) {
        StringBuilder sb = new StringBuilder();
        if (subtree != null) {
            // 遍历左子树
            sb.append(postOrder(subtree.getLeft()));
            // 遍历右子树
            sb.append(postOrder(subtree.getRight()));
            // 访问根结点
            sb.append(subtree.getData()).append(",");
        }
        return sb.toString();
    }

    @Override
    public String postOrder() {
        String sb = postOrder(root);
        if (sb.length() > 0) {
            // 去掉尾部","号
            sb = sb.substring(0, sb.length() - 1);
        }
        return sb;
    }

    // 方法:
    // 1. 入队第一个根结点
    // 2. 出队一个结点
    // 3. 马上入队这个结点的子结点,先左子结点再右子结点
    // 4. 重复2和3
    @Override
    public String levelOrder() {
        // 存放需要遍历的结点,左结点一定优先右节点遍历
        Queue<BinaryNode<T>> queue = new PriorityQueue<>();
        StringBuilder sb = new StringBuilder();
        BinaryNode<T> node = this.root;
        while (node != null) {
            // 记录经过的结点
            sb.append(node.getData());
            // 先按层次遍历结点,左结点一定在右结点之前访问
            if (node.getLeft() != null) {
                // 子结点入队
                queue.add(node.getLeft());
            }
            if (node.getRight() != null) {
                // 子结点入队
                queue.add(node.getRight());
            }
            // 访问下一个结点
            node = queue.poll();
        }

        return sb.toString();
    }

    // 非递归的先序遍历
    // 任何递归的本质,实际上就是入栈出栈的过程
    public String preOrder2() {
        StringBuilder sb = new StringBuilder();
        // 构建用于存放结点的栈
        Stack<BinaryNode<T>> stack = new Stack<>();
        BinaryNode<T> p = this.root;

        while (p != null || !stack.isEmpty()) {
            if (p != null) {
                // 访问根结点
                sb.append(p.getData()).append(",");
                // 将已访问过的结点入栈
                stack.push(p);
                // 继续访问其左子树
                p = p.getLeft();

            }
            //若p=null且栈不为空,则说明已沿左子树访问完一条路径, 从栈中弹出栈顶结点,并访问其右子树
            else {
                p = stack.pop();
                p = p.getRight();
            }
        }
        // 去掉最后一个逗号
        if (sb.length() > 0) {
            return sb.substring(0, sb.length() - 1);
        } else {
            return sb.toString();
        }
    }

    // 非递归的中序遍历
    public String inOrder2() {
        StringBuilder sb = new StringBuilder();
        // 构建用于存放结点的栈
        Stack<BinaryNode<T>> stack = new Stack<>();
        BinaryNode<T> p = this.root;

        while (p != null || !stack.isEmpty()) {
            // 把左子树都入栈,直到左子树为null
            while (p != null) {
                stack.push(p);
                p=p.getLeft();
            }
            // 如果栈不为空,因为前面左孩子已全部入栈
            if(!stack.isEmpty()){
                p = stack.pop();
                // 访问根结点
                sb.append(p.getData()).append(",");
                // 访问p结点的右子树
                p = p.getRight();
            }
        }
        if (sb.length() > 0) {
            return sb.substring(0, sb.length() - 1);
        } else {
            return sb.toString();
        }
    }

    // 非递归后序遍历
    public String postOrder2() {
        StringBuilder sb = new StringBuilder();
        // 构建用于存放结点的栈
        Stack<BinaryNode<T>> stack = new Stack<>();

        BinaryNode<T> p = this.root;
        BinaryNode<T> prev = this.root;

        while (p != null || !stack.isEmpty()) {
            // 把左子树都入栈,直到左子树为null
            while (p != null) {
                stack.push(p);
                p = p.getLeft();
            }

            // 开始访问当前结点的父结点的右子树
            if (!stack.isEmpty()) {
                // 获取子树
                // peek()函数返回栈顶的元素,但不弹出该栈顶元素
                BinaryNode<T> temp = stack.peek().getRight();
                // 先判断是否有右子树或者右子树是否已被访问过
                // 没有右子树,则可以记录当前根结点;
                // 右子树已被访问过,则可以记录当前根结点;
                if (temp == null || temp == prev) {
                    // 没有右子树或右子树已被访问过,则弹出父结点并访问
                    p = stack.pop();
                    // 访问根结点
                    sb.append(p.getData()).append(",");
                    // 记录已访问过的结点
                    prev = p;
                    // 置空当前结点
                    p = null;
                } else {
                    // 有子树,则开始遍历右子树
                    p = temp;
                }
            }
        }
        // 去掉最后一个逗号
        if (sb.length() > 0) {
            return sb.substring(0, sb.length() - 1);
        } else {
            return sb.toString();
        }
    }

    // 插入操作,递归实现
    private BinaryNode<T> insert(T data, BinaryNode<T> p) {
        if (p == null) {
            p = new BinaryNode<>(data, null, null);
        }
        int compareResult = data.compareTo(p.getData());
        // 相同元素不必重复插入
        // 如果小于0,从左子树遍历
        if (compareResult < 0) {
            p.setLeft(insert(data, p.getLeft()));
        }
        // 如果大于0,从右子树遍历
        else if (compareResult > 0) {
            p.setRight(insert(data, p.getRight()));
        }
        return p;
    }

    @Override
    public void insert(T data) {
        if (data == null) {
            throw new RuntimeException("data cannot be null!");
        }
        // 插入操作
        root = insert(data, root);
    }

    // 分3种情况
    // 1. 删除叶子结点(也就是没有孩子结点)
    // 2. 删除拥有一个孩子结点的结点(可能是左孩子也可能是右孩子)
    // 3. 删除拥有两个孩子结点的结点
    private BinaryNode<T> remove(T data, BinaryNode<T> p){
        //没有找到要删除的元素,递归结束
        if (p == null) {
            return null;
        }
        int compareResult = data.compareTo(p.getData());
        // 如果小于0,从左子树遍历
        if (compareResult < 0) {
            // 删除结点,更新子树
            p.setLeft(remove(data,p.getLeft()));
        }
        // 如果大于0,从右子树遍历
        else if (compareResult > 0) {
            // 删除结点,更新子树
            p.setRight(remove(data,p.getRight()));
        }
        // 3. 删除拥有两个孩子结点的结点
        else if (p.getLeft() != null && p.getRight() != null) {
            // 中继替换,找到右子树中最小的元素并替换需要删除的元素值
            p.setData(findMin(p.getRight()).getData());
            // 移除用于替换的结点
            p.setRight(remove(p.getData(), p.getRight()));
        }
        // 1. 删除叶子结点(也就是没有孩子结点)
        // 2. 删除拥有一个孩子结点的结点(可能是左孩子也可能是右孩子)
        else {
            // 直接删除结点,再把子树拼接上来
            p = (p.getLeft() != null) ? p.getLeft() : p.getRight();
        }
        // 返回该结点
        return p;
    }

    @Override
    public void remove(T data) {
        if(data == null) {
            throw new RuntimeException("data cannot be null!");
        }
        // 删除结点
        root = remove(data, root);
    }

    // 非递归删除
    // 分3种情况
    // 1. 删除叶子结点(也就是没有孩子结点)
    // 2. 删除拥有一个孩子结点的结点(可能是左孩子也可能是右孩子)
    // 3. 删除拥有两个孩子结点的结点
    public boolean remove2(T data) {
        if (data == null) {
            throw new RuntimeException("data cannot be null!");
        }
        //从根结点开始查找
        BinaryNode<T> current = this.root;
        //记录父结点
        BinaryNode<T> parent = this.root;
        //判断左右孩子的flag
        boolean isLeft = true;

        //找到要删除的结点
        while (data.compareTo(current.getData()) != 0) {
            //更新父结点记录
            parent = current;
            int result = data.compareTo(current.getData());
            // 如果小于0,从左子树遍历
            if (result < 0) {
                isLeft = true;
                current = current.getLeft();
            }
            // 如果大于0,从右子树遍历
            else if (result > 0) {
                isLeft = false;
                current = current.getRight();
            }
            // 如果没有找到,返回null
            if (current == null){
                return false;
            }
        }

        // 到这里说明已找到要删除的结点
        // 1. 删除叶子结点(也就是没有孩子结点)
        if (current.getLeft() == null && current.getRight() == null) {
            if (current == this.root) {
                this.root = null;
            } else if (isLeft) {
                parent.setLeft(null);
            } else {
                parent.setRight(null);
            }
        }
        // 2. 删除拥有一个右孩子结点的结点
        else if (current.getLeft() == null) {
            if (current == this.root) {
                this.root = current.getRight();
            }
            // current为parent的左子树
            else if (isLeft) {
                parent.setLeft(current.getRight());
            }
            // current为parent的右子树
            else {
                parent.setRight(current.getRight());
            }
        }
        // 2. 删除拥有一个左孩子结点的结点
        else if (current.getRight() == null) {
            if (current == this.root){
                this.root = current.getLeft();
            }
            // current为parent的左子树
            else if (isLeft) {
                parent.setLeft(current.getLeft());
            }
            // current为parent的右子树
            else {
                parent.setRight(current.getLeft());
            }
        }
        // 3. 删除拥有两个孩子结点的结点
        else {
            // 找到当前要删除结点current的右子树中的最小值元素
            BinaryNode<T> successor = findSuccessor(current);
            if(current == root) {
                this.root = successor;
            }
            // current为parent的左子树
            else if(isLeft) {
                parent.setLeft(successor);
            }
            // current为parent的右子树
            else {
                parent.setRight(successor);
            }
            // 把当前要删除的结点的左右子树赋值给successor
            successor.setLeft(current.getLeft());
            successor.setRight(current.getRight());
        }
        return true;
    }

    // 判断树T中是否包含data
    private boolean contains(T data, BinaryNode<T> p) {
        if (p == null || data == null) {
            return false;
        }
        // 计算比较结果
        int compareResult = data.compareTo(p.getData());
        // 如果小于0,从左子树遍历
        if (compareResult < 0) {
            return contains(data, p.getLeft());
        }
        // 如果大于0,从右子树遍历
        else if (compareResult > 0) {
            return contains(data, p.getRight());
        }
        // 如果等于0,即找到值
        else {
            return true;
        }
    }

    @Override
    public boolean contains(T data) {
        return contains(data, root);
    }

    // 查找最小值结点
    private BinaryNode<T> findMin(BinaryNode<T> p) {
        // 结束条件
        if (p == null) {
            return null;
        }
        // 遍历左子树即可
        // 如果没有左结点,那么t就是最小的
        else if (p.getLeft() == null) {
            return p;
        }
        return findMin(p.getLeft());
    }

    @Override
    public T findMin() {
        if (isEmpty()) {
            throw new RuntimeException("BinarySearchTree is empty!");
        }
        return findMin(root).getData();
    }

    // 查找最大值结点
    private BinaryNode<T> findMax(BinaryNode<T> p) {
        // 结束条件
        if (p == null) {
            return null;
        }
        else if (p.getRight() == null) {
            return p;
        }
        return findMax(p.getRight());
    }

    @Override
    public T findMax() {
        if (isEmpty()) {
            throw new RuntimeException("BinarySearchTree is empty!");
        }
        return findMax(root).getData();
    }

    // 根据data查找结点
    private BinaryNode<T> findNode(T data, BinaryNode<T> p) {
        if (p == null || data == null) {
            return null;
        }
        // 计算比较结果
        int compareResult = data.compareTo(p.getData());
        // 如果小于0,从左子树遍历
        if (compareResult < 0) {
            return findNode(data, p.getLeft());
        }
        // 如果大于0,从右子树遍历
        else if (compareResult > 0) {
            return findNode(data, p.getRight());
        }
        // 如果等于0,即找到值
        else {
            return p;
        }
    }

    @Override
    public BinaryNode<T> findNode(T data) {
        return findNode(data, root);
    }

    // 查找后继结点
    // 1. 存在右子树,找右子树最小值结点
    // 2. 不存在右子树,只能向上找(沿着到根结点的路径,可以理解为搜索该结点的路径)
    //     a. 若该结点是其父结点的左结点,则其后继结点是其父结点
    //     b. 若该结点是其父结点的右结点,则需要继续往上找,直到路径中出现第一个结点x是x的父结点的左结点,则该结点的后继结点是x的父结点
    @Override
    public BinaryNode<T> findSuccessor(BinaryNode<T> p) {
        if (p == null || p.getData() == null) {
            return null;
        }

        // 1. 存在右子树,找右子树最小值结点
        if (p.getRight() != null) {
            return findMin(p);
        }

        // 创建两个Stack,记录从根结点到当前结点中的所有结点和方向
        // true代表向左遍历,false代表向右遍历
        Stack<Boolean> directions = new Stack<>();
        Stack<BinaryNode<T>> nodes = new Stack<>();

        BinaryNode<T> current = root;
        nodes.push(current);
        while (current != null && (current.getLeft() != null || current.getRight() != null)) {

            // 计算比较结果
            int compareResult = p.getData().compareTo(current.getData());
            // 如果小于0,从左子树遍历
            if (compareResult < 0) {
                current = current.getLeft();
                directions.push(true);
                nodes.push(current);
            }
            // 如果大于0,从右子树遍历
            else if (compareResult > 0) {
                current = current.getRight();
                directions.push(false);
                nodes.push(current);
            }
            // 如果等于0,即找到值
            else {
                break;
            }
        }

        // 到这里路径已经记录好
        if (current == null) {
            return null;
        }
        while (!directions.isEmpty()) {
            // 若该结点是其父结点的右结点,则需要继续往上找
            if (!directions.peek()) {
                directions.pop();
                nodes.pop();
            }
            // 直到路径中出现第一个结点x是x的父结点的左结点,则该结点的后继结点是x的父结点
            else {
                // pop出当前结点,再pop出父结点
                nodes.pop();
                return nodes.pop();
            }
        }
        return null;
    }

    // 查找前驱结点
    // 1. 存在左子树,找左子树最大值结点
    // 2. 不存在左子树,只能向上找(沿着到根结点的路径,可以理解为搜索该结点的路径)
    //     a. 若该结点是其父结点的右结点,则其前驱结点是其父结点
    //     b. 若该结点是其父结点的左结点,则需要继续往上找,直到路径中出现第一个结点x是x的父结点的右结点,则该结点的后继结点是x的父结点
    @Override
    public BinaryNode<T> findPredecessor(BinaryNode<T> p) {
        if (p == null || p.getData() == null) {
            return null;
        }

        // 1. 存在左子树,找左子树最大值结点
        if (p.getLeft() != null) {
            return findMax(p);
        }

        // 创建两个Stack,记录从根结点到当前结点中的所有结点和方向
        // true代表向左遍历,false代表向右遍历
        Stack<Boolean> directions = new Stack<>();
        Stack<BinaryNode<T>> nodes = new Stack<>();

        BinaryNode<T> current = root;
        nodes.push(current);
        while (current != null && (current.getLeft() != null || current.getRight() != null)) {

            // 计算比较结果
            int compareResult = p.getData().compareTo(current.getData());
            // 如果小于0,从左子树遍历
            if (compareResult < 0) {
                current = current.getLeft();
                directions.push(true);
                nodes.push(current);
            }
            // 如果大于0,从右子树遍历
            else if (compareResult > 0) {
                current = current.getRight();
                directions.push(false);
                nodes.push(current);
            }
            // 如果等于0,即找到值
            else {
                break;
            }
        }

        // 到这里路径已经记录好
        if (current == null) {
            return null;
        }
        while (!directions.isEmpty()) {
            // 若该结点是其父结点的左结点,则需要继续往上找
            if (directions.peek()) {
                directions.pop();
                nodes.pop();
            }
            // 直到路径中出现第一个结点x是x的父结点的右结点,则该结点的后继结点是x的父结点
            else {
                // pop出当前结点,再pop出父结点
                nodes.pop();
                return nodes.pop();
            }
        }
        return null;
    }

    @Override
    public void clear() {
        root = null;
    }

    // 中序遍历的形式打印树
    private void printTree(BinaryNode<T> note) {
        if (note != null) {
            printTree(note.getLeft());
            System.out.println(note.getData());
            printTree(note.getRight());
        }
    }

    // 将树转换成字符串并打印在控制台上,用L表示左子树,R表示右子树
    @Override
    public void print() {
        LinkedList<BinaryNode<T>> tree = getCompleteBinaryTree();
        //获取树的深度
        int depth = height();
        Iterator<BinaryNode<T>> iterator = tree.iterator();

        int maxPosition = 1;

        // 层数,从1开始
        for (int floor = 1; floor <= depth; floor++) {
            // 左移相当于1*2^(floor-1)
            maxPosition = 1 << (floor - 1);
            //输出元素前需要打印的空白符
            //左移相当于1*2^( depth - floor ) - 1
            printBlank((1 << (depth - floor)) - 1);

            //开始打印元素
            for (int position = 0; position < maxPosition; position++) {
                if (iterator.hasNext()) {
                    BinaryNode<T> node = iterator.next();
                    if (node != null) {
                        System.out.print(node.getData());
                    } else {
                        System.out.print(" ");
                    }
                    //再次打印间隔空白符
                    printBlank((1 << (depth - floor + 1)) - 1);
                }
            }
            //换行
            System.out.println();
        }
    }

    // 打印空白
    private void printBlank(int length) {
        while (length -- > 0) {
            System.out.print(" ");
        }
    }

    // 将二叉树用空节点补充成完全二叉树,并以水平遍历形式返回
    private LinkedList<BinaryNode<T>> getCompleteBinaryTree() {
        Queue<BinaryNode<T>> queue = new LinkedList<>();
        LinkedList<BinaryNode<T>> tree = new LinkedList<>(); // 把树补充成完全二叉树,放在这个链表中
        queue.add(root);
        int nodeCount = 1; // 队列中非空节点数
        while (queue.size() > 0 && nodeCount > 0) {
            BinaryNode<T> node = queue.remove();
            if (node != null) {
                nodeCount--;
                tree.add(node);
                BinaryNode<T> left = node.getLeft();
                BinaryNode<T> right = node.getRight();
                if (left == null) {
                    queue.add(null);
                } else {
                    queue.add(left);
                    nodeCount++;
                }
                if (right == null) {
                    queue.add(null);
                } else {
                    queue.add(right);
                    nodeCount++;
                }
            } else {
                tree.add(null);
                queue.add(null);
                queue.add(null);
            }
        }
        return tree;
    }
}

要点

  • 对于二叉搜索树,可以通过中序遍历得到一个递增的有序序列
  • 前中后序遍历空间复杂度是O(h),h为二叉树高度,平均情况下,即树左右几乎平衡,h=logn,即O(logn);最坏情况下,即树严重左偏或右偏,h=n,即O(n)
⚠️ **GitHub.com Fallback** ⚠️