内容来自刘宇波老师算法与数据结构体系课
1、旋转图示






2、辅助函数
| Node 新添加了一个成员变量 height |
| |
| 辅助函数 |
| 1、获得节点 node 的高度 |
| 2、获得节点 node 的平衡因子 |
| 3、判断二叉树是否是一棵二分搜索树:中序遍历 |
| 4、判断二叉树是否是一棵平衡二叉树:前序遍历 |
| |
| 维护自平衡:LL、RR、LR、RL、L、R |
| 1、左旋转,旋转后要更新 height(必须先更新 y, 后更新 x) |
| 2、右旋转,旋转后要更新 height(必须先更新 y, 后更新 x) |
| |
| 在添加和删除时保持自平衡 |
| 1、更新 height |
| 2、计算平衡因子 balanceFactor |
| 3、维护自平衡 |
| |
| 删除节点后保持自平衡的坑 |
| 1、用待删除节点的后继节点或前驱节点来顶替待删除节点的位置时,也需要维护自平衡 |
| 2、删除叶子节点后, 返回的 retNode 为 null |
| |
| |
| |
| private int getHeight(Node node) { |
| if (node == null) return 0; |
| return node.height; |
| } |
| |
| |
| |
| |
| private int getBalanceFactor(Node node) { |
| if (node == null) return 0; |
| return getHeight(node.left) - getHeight(node.right); |
| } |
| |
| |
| |
| |
| public boolean isBST() { |
| ArrayList<K> keys = new ArrayList<>(); |
| inOrder(root, keys); |
| for (int i = 1; i < keys.size(); i++) { |
| if (keys.get(i - 1).compareTo(keys.get(i)) > 0) return false; |
| } |
| return true; |
| } |
| |
| |
| |
| |
| private void inOrder(Node node, ArrayList<K> keys) { |
| if (node == null) return; |
| |
| inOrder(node.left, keys); |
| keys.add(node.key); |
| inOrder(node.right, keys); |
| } |
| |
| |
| |
| |
| public boolean isBalanced() { |
| return isBalanced(root); |
| } |
| |
| |
| |
| |
| private boolean isBalanced(Node node) { |
| if (node == null) return true; |
| |
| int balanceFactor = getBalanceFactor(node); |
| if (Math.abs(balanceFactor) > 1) return false; |
| |
| return isBalanced(node.left) && isBalanced(node.right); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| private Node rightRotate(Node y) { |
| Node x = y.left; |
| Node t3 = x.right; |
| |
| |
| x.right = y; |
| y.left = t3; |
| |
| |
| y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1; |
| x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1; |
| |
| return x; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| private Node leftRotate(Node y) { |
| Node x = y.right; |
| Node t2 = x.left; |
| |
| |
| x.left = y; |
| y.right = t2; |
| |
| |
| y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1; |
| x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1; |
| |
| return x; |
| } |
3、实现 AVL 树
参考:深度分析 AVL 树的实现与优化
| |
| |
| |
| |
| public class AVLTree<K extends Comparable<K>, V> { |
| |
| private class Node { |
| public K key; |
| public V value; |
| public Node left; |
| public Node right; |
| public int height; |
| |
| public Node(K key, V value) { |
| this.key = key; |
| this.value = value; |
| this.left = null; |
| this.right = null; |
| this.height = 1; |
| } |
| } |
| |
| private Node root; |
| private int size; |
| |
| public AVLTree() { |
| root = null; |
| size = 0; |
| } |
| |
| |
| |
| |
| private int getHeight(Node node) { |
| if (node == null) return 0; |
| return node.height; |
| } |
| |
| |
| |
| |
| private int getBalanceFactor(Node node) { |
| if (node == null) return 0; |
| return getHeight(node.left) - getHeight(node.right); |
| } |
| |
| |
| |
| |
| public boolean isBST() { |
| ArrayList<K> keys = new ArrayList<>(); |
| inOrder(root, keys); |
| for (int i = 1; i < keys.size(); i++) { |
| if (keys.get(i - 1).compareTo(keys.get(i)) > 0) return false; |
| } |
| return true; |
| } |
| |
| |
| |
| |
| private void inOrder(Node node, ArrayList<K> keys) { |
| if (node == null) return; |
| |
| inOrder(node.left, keys); |
| keys.add(node.key); |
| inOrder(node.right, keys); |
| } |
| |
| |
| |
| |
| public boolean isBalanced() { |
| return isBalanced(root); |
| } |
| |
| |
| |
| |
| private boolean isBalanced(Node node) { |
| if (node == null) return true; |
| |
| int balanceFactor = getBalanceFactor(node); |
| if (Math.abs(balanceFactor) > 1) return false; |
| |
| return isBalanced(node.left) && isBalanced(node.right); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| private Node rightRotate(Node y) { |
| Node x = y.left; |
| Node t3 = x.right; |
| |
| |
| x.right = y; |
| y.left = t3; |
| |
| |
| y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1; |
| x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1; |
| |
| return x; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| private Node leftRotate(Node y) { |
| Node x = y.right; |
| Node t2 = x.left; |
| |
| |
| x.left = y; |
| y.right = t2; |
| |
| |
| y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1; |
| x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1; |
| |
| return x; |
| } |
| |
| |
| |
| |
| private Node getNode(Node node, K key) { |
| if (node == null) return null; |
| |
| if (key.compareTo(node.key) == 0) return node; |
| if (key.compareTo(node.key) < 0) return getNode(node.left, key); |
| return getNode(node.right, key); |
| } |
| |
| public void add(K key, V value) { |
| root = add(root, key, value); |
| } |
| |
| |
| |
| |
| private Node add(Node node, K key, V value) { |
| if (node == null) { |
| size++; |
| return new Node(key, value); |
| } |
| |
| if (key.compareTo(node.key) < 0) node.left = add(node.left, key, value); |
| else if (key.compareTo(node.key) > 0) node.right = add(node.right, key, value); |
| else node.value = value; |
| |
| node.height = Math.max(getHeight(node.left), getHeight(node.right)) + 1; |
| |
| int balanceFactor = getBalanceFactor(node); |
| |
| if (balanceFactor > 1 && getBalanceFactor(node.left) >= 0) return rightRotate(node); |
| if (balanceFactor < -1 && getBalanceFactor(node.right) <= 0) return leftRotate(node); |
| if (balanceFactor > 1 && getBalanceFactor(node.left) < 0) { |
| |
| node.left = leftRotate(node.left); |
| return rightRotate(node); |
| } |
| if (balanceFactor < -1 && getBalanceFactor(node.right) > 0) { |
| |
| node.right = rightRotate(node.right); |
| return leftRotate(node); |
| } |
| |
| return node; |
| } |
| |
| |
| |
| |
| private Node minimum(Node node) { |
| if (node.left == null) return node; |
| return minimum(node.left); |
| } |
| |
| |
| |
| |
| private Node removeMin(Node node) { |
| if (node.left == null) { |
| Node rightNode = node.right; |
| node.right = null; |
| size--; |
| return rightNode; |
| } |
| |
| node.left = removeMin(node.left); |
| return node; |
| } |
| |
| public V remove(K key) { |
| Node node = getNode(root, key); |
| if (node != null) { |
| root = remove(root, key); |
| return node.value; |
| } |
| return null; |
| } |
| |
| |
| |
| |
| private Node remove(Node node, K key) { |
| if (node == null) return null; |
| |
| Node retNode; |
| if (key.compareTo(node.key) < 0) { |
| node.left = remove(node.left, key); |
| retNode = node; |
| } else if (key.compareTo(node.key) > 0) { |
| node.right = remove(node.right, key); |
| retNode = node; |
| } else { |
| if (node.left == null) { |
| Node rightNode = node.right; |
| node.right = null; |
| size--; |
| retNode = rightNode; |
| } else if (node.right == null) { |
| Node leftNode = node.left; |
| node.left = null; |
| size--; |
| retNode = leftNode; |
| } else { |
| Node successor = minimum(node.right); |
| |
| successor.right = remove(node.right, successor.key); |
| successor.left = node.left; |
| node.left = node.right = null; |
| retNode = successor; |
| } |
| } |
| |
| if (retNode == null) return null; |
| retNode.height = Math.max(getHeight(retNode.left), getHeight(retNode.right)) + 1; |
| |
| int balanceFactor = getBalanceFactor(retNode); |
| |
| if (balanceFactor > 1 && getBalanceFactor(retNode.left) >= 0) return rightRotate(retNode); |
| if (balanceFactor < -1 && getBalanceFactor(retNode.right) <= 0) return leftRotate(retNode); |
| if (balanceFactor > 1 && getBalanceFactor(retNode.left) < 0) { |
| |
| retNode.left = leftRotate(retNode.left); |
| return rightRotate(retNode); |
| } |
| if (balanceFactor < -1 && getBalanceFactor(retNode.right) > 0) { |
| |
| retNode.right = rightRotate(retNode.right); |
| return leftRotate(retNode); |
| } |
| |
| return retNode; |
| } |
| |
| public boolean contains(K key) { |
| return getNode(root, key) != null; |
| } |
| |
| public V get(K key) { |
| Node node = getNode(root, key); |
| return node != null ? node.value : null; |
| } |
| |
| public void set(K key, V newValue) { |
| Node node = getNode(root, key); |
| if (node != null) node.value = newValue; |
| else throw new IllegalArgumentException(key + " doesn't exist!"); |
| } |
| |
| public int getSize() { |
| return size; |
| } |
| |
| public boolean isEmpty() { |
| return size == 0; |
| } |
| } |
4、AVL 集合
| public class AVLSet<E extends Comparable<E>> implements Set<E> { |
| |
| private final AVLTree<E, Object> avlTree; |
| |
| public AVLSet() { |
| avlTree = new AVLTree<>(); |
| } |
| |
| @Override |
| public void add(E e) { |
| avlTree.add(e, null); |
| } |
| |
| @Override |
| public void remove(E e) { |
| avlTree.remove(e); |
| } |
| |
| @Override |
| public boolean contains(E e) { |
| return avlTree.contains(e); |
| } |
| |
| @Override |
| public int getSize() { |
| return avlTree.getSize(); |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return avlTree.isEmpty(); |
| } |
| } |
5、AVL 映射
| public class AVLMap<K extends Comparable<K>, V> implements Map<K, V> { |
| |
| private final AVLTree<K, V> avlTree; |
| |
| public AVLMap() { |
| avlTree = new AVLTree<>(); |
| } |
| |
| @Override |
| public void add(K key, V value) { |
| avlTree.add(key, value); |
| } |
| |
| @Override |
| public V remove(K key) { |
| return avlTree.remove(key); |
| } |
| |
| @Override |
| public boolean contains(K key) { |
| return avlTree.contains(key); |
| } |
| |
| @Override |
| public V get(K key) { |
| return avlTree.get(key); |
| } |
| |
| @Override |
| public void set(K key, V newValue) { |
| avlTree.set(key, newValue); |
| } |
| |
| @Override |
| public int getSize() { |
| return avlTree.getSize(); |
| } |
| |
| @Override |
| public boolean isEmpty() { |
| return avlTree.isEmpty(); |
| } |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步