目录
二分搜索树
引言
基础二叉树的衍生结构在实际工程有着广泛的应用
堆——完全二叉树
AVL、RBTree、2-3树、B树——搜索树
BST:二分搜索树(二叉搜索树、二叉排序树),查找操作。
1. 也是个二叉树2. 每个树的左子树的所有节点值 < 树根节点 < 所有右子树的节点值(当前树中所有子树仍然遵循这个规则)。JDK中的BST,不存在重复节点。
3. 存储结点必须具备可以比较的能力(实现Comparable接口 或 传入比较器)
基础操作
1. 向BST中添加一个节点
新插入的元素都一定在树的叶子结点进行插入操作
不断和数根节点去比大小,若小就在左树中添加,若大就在右子树添加递归上述过程
/** * 当前以root为根的BST树中插入一个元素val,返回插入后的树根 */ private Node add(Node root, int val) { if (root == null) { //此时树为空,node就是树根 Node node = new Node(val); size++; return node; } if (val < root.val) { //在左子树中插入 root.left = add(root.left, val); } if (val > root.val) { //在右子树中插入 root.right = add(root.right, val); } return root; }
时间复杂度
平均: O(logn)——树的插入
最坏:O(n)。假设此时插入的是一个完全有序的集合[1,2,3,4,],插入5,此时二分搜索树变为了单枝树,退化成了链表,还是在右侧插入。
2. 判断val是否在BST中存在contains
不断递归的去和树的树根相比
val == root.val,找到了
val < root.val,继续在左树中找
val > root.val,继续在右树中找
root == null,根本不存在这个val
这个查找过程就是—个"二分搜索"/** * 判断以当前root为根的BST中是否包含指定值val。存在返回true,否则返回false */ private boolean contains(Node root, int val) { if (root == null) { return false; } if (val == root.val) { return true; } else if (val < root.val) { //在左子树中找 return contains(root.left, val); } else { //在右子树中找 return contains(root.right, val); } }
时间复杂度:平均:O(logn)
最坏:退化成链表,单枝树,链表的遍历O(n)
3. 按照先序遍历的方式打印当前的BST
@Override public String toString() { StringBuilder sb = new StringBuilder(); generateBSTString(root, 0, sb); return sb.toString(); } /** * 按照先序遍历将BST的节点值存入sb之中 */ private void generateBSTString(Node root, int height, StringBuilder sb) { // 边界 if (root == null) { sb.append(generateHeightStr(height)).append("NULL\n"); return; } sb.append(generateHeightStr(height)).append(root.val).append("NULL\n"); // 递归打印左子树 generateBSTString(root.left,height+1,sb); // 递归打印右子树 generateBSTString(root.right,height+1,sb); } /** * 按照当前节点的高度打印 -- * root.left => -- * 第二层 ---- * * @param height */ private String generateHeightStr(int height) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < height; i++) { sb.append("--"); } return sb.toString(); }
4. 找到一颗BST的最大值和最小值
问题:BST的最大值和最小值一定都在叶子结点?
答:错误
在BST中最小值结点,是先序遍历中第一个左子树为空的结点。
public int findMin() { if (size == 0) { throw new NoSuchElementException("bst is empty!no element!"); } return min(root).val; } /** * 以root为根的BST中找到最小值节点 */ private Node min(Node root) { if (root.left == null) { // 这个节点就是第一个碰到左子树为为空的节点 return root; } return min(root.left); }
在BST中最大值结点,是先序遍历中第一个右子树为空的结点。
public int findMax() { if (size == 0) { throw new NoSuchElementException("bst is empty!no element!"); } return max(root).val; } /** * 以root为根的BST中找到最大值节点 */ private Node max(Node root) { if (root.right == null) { // 这个节点就是第一个碰到右子树为为空的节点 return root; } return max(root.right); }
5. 删除最大值和最小值
删除最小值
public int removeMin() { if (size == 0) { throw new NoSuchElementException("bst is empty!cannot remove!"); } Node minNode = min(root); root = removeMin(root); return minNode.val; } /** * 传入一棵以root为根的BST,就能删除其中的最小值,返回删除后的树根节点 */ private Node removeMin(Node root) { // 边界 if (root.left == null) { // root就是待删除的节点 Node right = root.right; root.left = root.right = root = null; size--; return right; } // 左树删除,更新左子树的引用 root.left = removeMin(root.left); return root; }
删除最大值
public int removeMax() { if (size == 0) { throw new NoSuchElementException("bst is empty!cannot remove!"); } Node max = max(root); root = removeMax(root); return max.val; } /** * 删除以root为根的BST中的最大值节点,返回删除后的树根节点 */ private Node removeMax(Node root) { if (root == null) { // root就是待删除的节点 Node left = root.left; root.left = root.right = root = null; size--; return left; } // 此时root不是待删除的节点,待删节点一定在右子树删除, // 更新右子树的引用 root.right = removeMax(root.right); return root; }
6. 删除任意值val
代码实现
public void remove(int val) { // 边界 if (size == 0) { throw new NoSuchElementException("bst is empty!cannot remove!"); } root = remove(root, val); } /** * 在当前以root为根的bst中删除值为val的节点 * 返回删除后的新的树根节点 */ private Node remove(Node root, int val) { if (root == null) { // 将树中所有节点全都遍历一遍后还没有找到值为val的节点,节点根本不存在 throw new NoSuchElementException("bst has not this node!"); } else if (val < root.val) { //在左子树中删除 root.left = remove(root.left, val); return root; } else if (val > root.val) { //在右子树中删除 root.right = remove(root.right, val); return root; } else { // 树不为空,且root.val == val // 当前root就是待删除的节点 if (root.left == null) { // 当前待删除节点的左树为空,类似删除最小值,返回右子树的树根 Node right = root.right; root.right = root = null; size--; return right; } if (root.right == null) { // 当前待删除节点的右树为空,类似删除最大值,返回左子树的树根 Node left = root.left; root.left = root = null; size--; return left; } // 这个待删除的结点左子树和右子树都不为空,Hibbard Deletion // 找到当然root的后继结点,右子树的最小值结点 Node successor = min(root.right); // 1.先在右子树中删除这个点 successor.right = removeMin(root.right); // 2.连接root.left successor.left = root.left; //gc root.left = root.right = root = null; // 3.返回successor return successor; } }