二叉搜索树完全实现

二叉搜索树,又叫二叉查找树(Binary Search Tree);

请问你思考过没有?有了哈希表,为什么我们还需要二叉树?

其中最主要的一个原因,就是哈希表如果要按照顺序输出的时候,就比较麻烦,因为元素分布是无序的,而二叉树的特殊形态 ---- 二叉搜索树就比较简单了,只需要中序遍历就好了。

而hashmap的底层红黑树其实也是一种特殊的二叉搜索树;也许有的同学很想啃下来红黑树,当面试的时候面试官问起hashmap源码的时候做到游刃有余,那么二叉搜索树算是前面的一个小BOSS。

什么是二叉搜索树

二叉搜索树是一种特殊的二叉树;二叉查找树要求,在树中的任意一个节点,其左子树中的每个节点的值,都要小于这个节点的值,而右子树节点的值都大于这个节点的值。

那么为什么二叉搜索树有了这种特性之后中序遍历就一定是按照顺序的呢?因为中序遍历就是树上的任意一个节点,左子树上面的所有值都在这个节点输出之前输出,右子树上面的所有节点的值都在这个节点输出之后输出,所以一定是有序的。

中序遍历伪代码

    public List<Integer> order(TreeNode root, List<Integer> res) {
        if (Objects.nonNull(root)) {
            order(root.left, res);
            res.add(root.val);
            order(root.right, res);
        }
        return res;
    }

如何完成二叉搜索树基本的操作

我们完成的二叉搜索树是一棵没有重复元素的二叉搜索树。

二叉搜索树的基本操作可以分为增、删、查。修改可以看做先删再插。

添加元素的操作

增加相对来说比较简单,我们先看一下树的根节点是不是有值,如果没有值的话,就把当前的元素赋值为根节点即可;如果有值的话,确认一个指针point,指向根节点,然后比教指针上面的值和待插入元素的值:

  • 如果指针的元素的值比较大,那么判断指针的左子节点是否为空,为空的话就将待插入节点当做指针节点的左子节点,并完成增加操作,跳出循环;不是空的话,指针就指向指针的左子节点。
  • 如果指针的元素的值比较小,那么判断指针的右子节点是否为空,为空的话就将待插入节点当做指针节点的右子节点,并完成增加操作,跳出循环;不是空的话,指针就指向指针的右子节点。

查找元素的操作

查找的话和插入差不多,也是定义一个指针节点指向根节点。定义一个循环,比较指针节点的值和代插入节点的值,如果待插入的节点比指针节点大,那么指针就指向指针的右子节点,反之则指向左子节点,如果相等就返回,跳出循环。

删除元素的操作

删除的话,是比较复杂的情况。我们需要和查找的方法一样,首先找到待删除的节点,然后判断这个节点是否有左右子节点,如果没有左子节点的话,就直接把待删除节点的父节点的子节点设置为待删除节点的右子节点。如果没有右子节点的话,就直接把待删除节点的父节点的子节点设置为待删除节点的左子节点。

如果既有左子节点也有右子节点的话,那么就要找到待删除节点的后继节点(一个节点的后继节点就是比当前节点的值大的所有节点中最小的那个,可以保证这个节点是不会有左子节点的)。然后把待删除的节点的值设置成这个后继节点的值,等遍历到了这个后继节点的时候,把它的父节点的子节点设置成这个节点的右子节点(因为它肯定没有左子树)。

同理,也可以操作待删除节点的前驱节点,大家可以试一下。

完整代码

package com.darwin.algorithm.tree;

import com.darwin.algorithm.tree.util.TreeNode;
import sun.reflect.generics.tree.Tree;

import java.util.*;

/**
 * 二叉搜索树的操作
 */
public class BinarySearchTree {

    private TreeNode root;

    public void insert(TreeNode node) {
        if (root == null) {
            root = node;
            return;
        }
        TreeNode point = root;
        while (true) {
            if (point.val < node.val) {
                if (point.right == null) {
                    point.right = node;
                    return;
                }
                point = point.right;
            } else if (point.val > node.val) {
                if (point.left == null) {
                    point.left = node;
                    return;
                }
                point = point.left;
            }
        }
    }

    public TreeNode find(int val) {
        TreeNode point = root;
        while (point != null) {
            if (point.val > val) {
                point = point.left;
                continue;
            }
            if (point.val < val) {
                point = point.right;
                continue;
            }
            return point;
        }
        return null;
    }
	
    public TreeNode deleteNode(TreeNode root, int val) {
        if (root == null) {
            return null;
        }
        if (val > root.val) {
            root.right = deleteNode(root.right, val);
        } else if (val < root.val) {
            root.left = deleteNode(root.left, val);
        } else {
            if (root.right == null) {
                root = root.left;
            } else if (root.left == null) {
                root = root.right;
            } else {
                TreeNode right = root.right;
                TreeNode left = root.left;
                TreeNode successor = successor(root);
                right = deleteSuccessor(right);
                root = successor;
                root.left = left;
                root.right = right;
            }
        }
        return root;
    }

    public TreeNode successor(TreeNode root) {
        root = root.right;
        while (root.left != null) root = root.left;
        return root;
    }

   private TreeNode deleteSuccessor(TreeNode node) {
        if (node.left == null) {
            return node.right;
        }
        node.left = deleteSuccessor(node.left);
        return node;
    }


    public static void main(String[] args) {
        BinarySearchTree tree = new BinarySearchTree();
        tree.insert(new TreeNode(2));
        tree.insert(new TreeNode(1));
        tree.insert(new TreeNode(3));
        tree.insert(new TreeNode(4));
        System.out.println(TraverseWayEnum.PRE_ORDER.order(tree.root, new LinkedList<>()));
        System.out.println(tree.find(4).val);
    }
}

/**
 * 二叉树遍历方式枚举类
 */
enum TraverseWayEnum {
    /**
     * 前序遍历
     */
    PRE_ORDER {
        @Override
        public List<Integer> order(TreeNode root, List<Integer> res) {
            if (Objects.nonNull(root)) {
                res.add(root.val);
                order(root.left, res);
                order(root.right, res);
            }
            return res;
        }
    },
    /**
     * 中序遍历
     */
    IN_ORDER {
        @Override
        public List<Integer> order(TreeNode root, List<Integer> res) {
            if (Objects.nonNull(root)) {
                order(root.left, res);
                res.add(root.val);
                order(root.right, res);
            }
            return res;
        }
    },
    /**
     * 后序遍历
     */
    POST_ORDER {
        @Override
        public List<Integer> order(TreeNode root, List<Integer> res) {
            if (Objects.nonNull(root)) {
                order(root.left, res);
                order(root.right, res);
                res.add(root.val);
            }
            return res;
        }
    },
    /**
     * 层次遍历
     */
    LEVEL_ORDER {
        @Override
        public List<Integer> order(TreeNode root, List<Integer> res) {
            if (Objects.isNull(root)) {
                return new LinkedList<>();
            }
            Queue<TreeNode> oddQueue = new LinkedList<>();
            Queue<TreeNode> evenQueue = new LinkedList<>();
            oddQueue.add(root);
            while (!oddQueue.isEmpty() || !evenQueue.isEmpty()) {
                helpLevelOrder(res, oddQueue, evenQueue);
                helpLevelOrder(res, evenQueue, evenQueue);
            }
            return res;
        }
    },
    /**
     * 之字形遍历
     */
    ZIGZAG_LEVEL_ORDER {
        @Override
        public List<Integer> order(TreeNode root, List<Integer> res) {
            if (Objects.isNull(root)) {
                return new LinkedList<>();
            }
            Stack<TreeNode> oddStack = new Stack<>();
            Stack<TreeNode> evenStack = new Stack<>();
            oddStack.add(root);
            while (!oddStack.empty() || !evenStack.empty()) {
                while (!oddStack.empty()) {
                    TreeNode pop = oddStack.pop();
                    if (Objects.nonNull(pop.left)) {
                        evenStack.add(pop.left);
                    }
                    if (Objects.nonNull(pop.right)) {
                        evenStack.add(pop.right);
                    }
                    res.add(pop.val);
                }
                while (!evenStack.empty()) {
                    TreeNode pop = evenStack.pop();
                    if (Objects.nonNull(pop.right)) {
                        oddStack.add(pop.right);
                    }
                    if (Objects.nonNull(pop.left)) {
                        oddStack.add(pop.left);
                    }
                    res.add(pop.val);
                }
            }
            return res;
        }
    };

    private static void helpLevelOrder(List<Integer> res, Queue<TreeNode> oneQueue, Queue<TreeNode> secondQueue) {
        while (!oneQueue.isEmpty()) {
            TreeNode poll = oneQueue.poll();
            if (Objects.nonNull(poll.left)) {
                secondQueue.add(poll.left);
            }
            if (Objects.nonNull(poll.right)) {
                secondQueue.add(poll.right);
            }
            res.add(poll.val);
        }
    }

    public abstract List<Integer> order(TreeNode root, List<Integer> res);
}