|
| 1 | +import java.util.ArrayList; |
| 2 | + |
| 3 | +public class AVLTree<K extends Comparable<K>, V> { |
| 4 | + |
| 5 | + private class Node { |
| 6 | + public K key; |
| 7 | + public V value; |
| 8 | + public Node left, right; |
| 9 | + public int height; |
| 10 | + |
| 11 | + public Node(K key, V value) { |
| 12 | + this.key = key; |
| 13 | + this.value = value; |
| 14 | + left = null; |
| 15 | + right = null; |
| 16 | + height = 1; |
| 17 | + } |
| 18 | + } |
| 19 | + |
| 20 | + private Node root; |
| 21 | + private int size; |
| 22 | + |
| 23 | + public AVLTree() { |
| 24 | + root = null; |
| 25 | + size = 0; |
| 26 | + } |
| 27 | + |
| 28 | + public int getSize() { |
| 29 | + return size; |
| 30 | + } |
| 31 | + |
| 32 | + public boolean isEmpty() { |
| 33 | + return size == 0; |
| 34 | + } |
| 35 | + |
| 36 | + // 判断该二叉树是否是一棵二分搜索树 |
| 37 | + public boolean isBST() { |
| 38 | + ArrayList<K> keys = new ArrayList<>(); |
| 39 | + inOrder(root, keys); |
| 40 | + for (int i = 1; i < keys.size(); i++) { |
| 41 | + if (keys.get(i - 1).compareTo(keys.get(i)) > 0) { |
| 42 | + return false; |
| 43 | + } |
| 44 | + } |
| 45 | + return true; |
| 46 | + } |
| 47 | + |
| 48 | + private void inOrder(Node node, ArrayList<K> keys) { |
| 49 | + if (node == null) { |
| 50 | + return; |
| 51 | + } |
| 52 | + inOrder(node.left, keys); |
| 53 | + keys.add(node.key); |
| 54 | + inOrder(node.right, keys); |
| 55 | + } |
| 56 | + |
| 57 | + // 判断该二叉树是否是一棵平衡二叉树 |
| 58 | + public boolean isBalanced() { |
| 59 | + return isBalanced(root); |
| 60 | + } |
| 61 | + |
| 62 | + // 判断以Node节点为根的二叉树是否是一棵平衡二叉树,递归算法 |
| 63 | + private boolean isBalanced(Node node) { |
| 64 | + if (node == null) { |
| 65 | + return true; |
| 66 | + } |
| 67 | + int balanceFactor = getBalanceFactor(node); |
| 68 | + if (Math.abs(balanceFactor) > 1) { |
| 69 | + return false; |
| 70 | + } |
| 71 | + return isBalanced(node.left) && isBalanced(node.right); |
| 72 | + } |
| 73 | + |
| 74 | + private int getHeight(Node node) { |
| 75 | + if (node == null) |
| 76 | + return 0; |
| 77 | + return node.height; |
| 78 | + } |
| 79 | + |
| 80 | + // 获得节点node的平衡因子 |
| 81 | + private int getBalanceFactor(Node node) { |
| 82 | + if (node == null) |
| 83 | + return 0; |
| 84 | + |
| 85 | + return getHeight(node.left) - getHeight(node.right); |
| 86 | + } |
| 87 | + |
| 88 | + // 对节点y进行向右旋转操作,返回旋转后新的根节点x |
| 89 | + private Node rightRotate(Node y) { |
| 90 | + Node x = y.left; |
| 91 | + Node T3 = x.right; |
| 92 | + |
| 93 | + // 向右旋转过程 |
| 94 | + x.right = y; |
| 95 | + y.left = T3; |
| 96 | + |
| 97 | + // 更新 height (只需要更新 y和x, 先y后x) |
| 98 | + y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1; |
| 99 | + x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1; |
| 100 | + |
| 101 | + return x; |
| 102 | + } |
| 103 | + |
| 104 | + // 对节点y进行向左旋转操作,返回旋转后新的根节点x |
| 105 | + private Node leftRotate(Node y) { |
| 106 | + Node x = y.right; |
| 107 | + Node T2 = x.left; |
| 108 | + |
| 109 | + // 向左旋转过程 |
| 110 | + x.left = y; |
| 111 | + y.right = T2; |
| 112 | + |
| 113 | + // 更新 height (只需要更新 y和x, 先y后x) |
| 114 | + y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1; |
| 115 | + x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1; |
| 116 | + |
| 117 | + return x; |
| 118 | + } |
| 119 | + |
| 120 | + // 向二分搜索树中添加新的元素(key, value) |
| 121 | + public void add(K key, V value) { |
| 122 | + root = add(root, key, value); |
| 123 | + } |
| 124 | + |
| 125 | + // 向以node为根的二分搜索树中插入元素(key, value),递归算法 |
| 126 | + // 返回插入新节点后二分搜索树的根 |
| 127 | + private Node add(Node node, K key, V value) { |
| 128 | + |
| 129 | + if (node == null) { |
| 130 | + size++; |
| 131 | + return new Node(key, value); |
| 132 | + } |
| 133 | + |
| 134 | + if (key.compareTo(node.key) < 0) |
| 135 | + node.left = add(node.left, key, value); |
| 136 | + else if (key.compareTo(node.key) > 0) |
| 137 | + node.right = add(node.right, key, value); |
| 138 | + else // key.compareTo(node.key) == 0 |
| 139 | + node.value = value; |
| 140 | + |
| 141 | + // 更新 height |
| 142 | + node.height = 1 + Math.max(getHeight(node.left), getHeight(node.right)); |
| 143 | + |
| 144 | + // 计算平衡因子 |
| 145 | + int balanceFactor = getBalanceFactor(node); |
| 146 | +// if (Math.abs(balanceFactor) > 1) { |
| 147 | +// System.out.println("unbabalanced : " + balanceFactor); |
| 148 | +// } |
| 149 | + |
| 150 | + // 平衡维护 |
| 151 | + // LL |
| 152 | + if (balanceFactor > 1 && getBalanceFactor(node.left) >= 0) { |
| 153 | + return rightRotate(node); |
| 154 | + } |
| 155 | + // RR |
| 156 | + if (balanceFactor < -1 && getBalanceFactor(node.right) <= 0) { |
| 157 | + return leftRotate(node); |
| 158 | + } |
| 159 | + // LR |
| 160 | + if (balanceFactor > 1 && getBalanceFactor(node.left) < 0) { |
| 161 | + node.left = leftRotate(node.left); // 转化成LL |
| 162 | + return rightRotate(node); |
| 163 | + } |
| 164 | + // RL |
| 165 | + if (balanceFactor < -1 && getBalanceFactor(node.right) > 0) { |
| 166 | + node.right = rightRotate(node.right);// 转化成RR |
| 167 | + return leftRotate(node); |
| 168 | + } |
| 169 | + |
| 170 | + return node; |
| 171 | + } |
| 172 | + |
| 173 | + // 返回以node为根节点的二分搜索树中,key所在的节点 |
| 174 | + private Node getNode(Node node, K key) { |
| 175 | + |
| 176 | + if (node == null) |
| 177 | + return null; |
| 178 | + |
| 179 | + if (key.equals(node.key)) |
| 180 | + return node; |
| 181 | + else if (key.compareTo(node.key) < 0) |
| 182 | + return getNode(node.left, key); |
| 183 | + else // if(key.compareTo(node.key) > 0) |
| 184 | + return getNode(node.right, key); |
| 185 | + } |
| 186 | + |
| 187 | + public boolean contains(K key) { |
| 188 | + return getNode(root, key) != null; |
| 189 | + } |
| 190 | + |
| 191 | + public V get(K key) { |
| 192 | + |
| 193 | + Node node = getNode(root, key); |
| 194 | + return node == null ? null : node.value; |
| 195 | + } |
| 196 | + |
| 197 | + public void set(K key, V newValue) { |
| 198 | + Node node = getNode(root, key); |
| 199 | + if (node == null) |
| 200 | + throw new IllegalArgumentException(key + " doesn't exist!"); |
| 201 | + |
| 202 | + node.value = newValue; |
| 203 | + } |
| 204 | + |
| 205 | + // 返回以node为根的二分搜索树的最小值所在的节点 |
| 206 | + private Node minimum(Node node) { |
| 207 | + if (node.left == null) |
| 208 | + return node; |
| 209 | + return minimum(node.left); |
| 210 | + } |
| 211 | + |
| 212 | + // 删除掉以node为根的二分搜索树中的最小节点 |
| 213 | + // 返回删除节点后新的二分搜索树的根 |
| 214 | + private Node removeMin(Node node) { |
| 215 | + |
| 216 | + if (node.left == null) { |
| 217 | + Node rightNode = node.right; |
| 218 | + node.right = null; |
| 219 | + size--; |
| 220 | + return rightNode; |
| 221 | + } |
| 222 | + |
| 223 | + node.left = removeMin(node.left); |
| 224 | + return node; |
| 225 | + } |
| 226 | + |
| 227 | + // 从二分搜索树中删除键为key的节点 |
| 228 | + public V remove(K key) { |
| 229 | + |
| 230 | + Node node = getNode(root, key); |
| 231 | + if (node != null) { |
| 232 | + root = remove(root, key); |
| 233 | + return node.value; |
| 234 | + } |
| 235 | + return null; |
| 236 | + } |
| 237 | + |
| 238 | + private Node remove(Node node, K key) { |
| 239 | + |
| 240 | + if (node == null) |
| 241 | + return null; |
| 242 | + |
| 243 | + Node retNode; |
| 244 | + if (key.compareTo(node.key) < 0) { |
| 245 | + node.left = remove(node.left, key); |
| 246 | + retNode = node; |
| 247 | + } else if (key.compareTo(node.key) > 0) { |
| 248 | + node.right = remove(node.right, key); |
| 249 | + retNode = node; |
| 250 | + } else { // key.compareTo(node.key) == 0 |
| 251 | + |
| 252 | + // 待删除节点左子树为空的情况 |
| 253 | + if (node.left == null) { |
| 254 | + Node rightNode = node.right; |
| 255 | + node.right = null; |
| 256 | + size--; |
| 257 | + retNode = rightNode; |
| 258 | + } |
| 259 | + |
| 260 | + // 待删除节点右子树为空的情况 |
| 261 | + else if (node.right == null) { |
| 262 | + Node leftNode = node.left; |
| 263 | + node.left = null; |
| 264 | + size--; |
| 265 | + retNode = leftNode; |
| 266 | + } else { // 待删除节点左右子树均不为空的情况 |
| 267 | + |
| 268 | + // 找到比待删除节点大的最小节点, 即待删除节点右子树的最小节点 |
| 269 | + // 用这个节点顶替待删除节点的位置 |
| 270 | + Node successor = minimum(node.right); |
| 271 | +// successor.right = removeMin(node.right); // 注意平衡 |
| 272 | + successor.right = remove(node.right, successor.key); // 注意平衡 |
| 273 | + successor.left = node.left; |
| 274 | + |
| 275 | + node.left = node.right = null; |
| 276 | + |
| 277 | + retNode = successor; |
| 278 | + } |
| 279 | + |
| 280 | + } |
| 281 | + if (retNode == null) // 注意空处理 |
| 282 | + return null; |
| 283 | + |
| 284 | + // 更新 height |
| 285 | + retNode.height = 1 + Math.max(getHeight(retNode.left), getHeight(retNode.right)); |
| 286 | + |
| 287 | + // 计算平衡因子 |
| 288 | + int balanceFactor = getBalanceFactor(retNode); |
| 289 | +// if (Math.abs(balanceFactor) > 1) { |
| 290 | +// System.out.println("unbabalanced : " + balanceFactor); |
| 291 | +// } |
| 292 | + |
| 293 | + // 平衡维护 |
| 294 | + // LL |
| 295 | + if (balanceFactor > 1 && getBalanceFactor(retNode.left) >= 0) { |
| 296 | + return rightRotate(retNode); |
| 297 | + } |
| 298 | + // RR |
| 299 | + if (balanceFactor < -1 && getBalanceFactor(retNode.right) <= 0) { |
| 300 | + return leftRotate(retNode); |
| 301 | + } |
| 302 | + // LR |
| 303 | + if (balanceFactor > 1 && getBalanceFactor(retNode.left) < 0) { |
| 304 | + retNode.left = leftRotate(retNode.left); // 转化成LL |
| 305 | + return rightRotate(retNode); |
| 306 | + } |
| 307 | + // RL |
| 308 | + if (balanceFactor < -1 && getBalanceFactor(retNode.right) > 0) { |
| 309 | + retNode.right = rightRotate(retNode.right);// 转化成RR |
| 310 | + return leftRotate(retNode); |
| 311 | + } |
| 312 | + |
| 313 | + return retNode; |
| 314 | + } |
| 315 | + |
| 316 | + public static void main(String[] args) { |
| 317 | + |
| 318 | + System.out.println("Pride and Prejudice"); |
| 319 | + |
| 320 | + ArrayList<String> words = new ArrayList<>(); |
| 321 | + if (FileOperation.readFile("AVLTree/pride-and-prejudice.txt", words)) { |
| 322 | + System.out.println("Total words: " + words.size()); |
| 323 | + |
| 324 | + AVLTree<String, Integer> map = new AVLTree<>(); |
| 325 | + for (String word : words) { |
| 326 | + if (map.contains(word)) |
| 327 | + map.set(word, map.get(word) + 1); |
| 328 | + else |
| 329 | + map.add(word, 1); |
| 330 | + } |
| 331 | + |
| 332 | + System.out.println("Total different words: " + map.getSize()); |
| 333 | + System.out.println("Frequency of PRIDE: " + map.get("pride")); |
| 334 | + System.out.println("Frequency of PREJUDICE: " + map.get("prejudice")); |
| 335 | + |
| 336 | + System.out.println("is BST : " + map.isBST()); |
| 337 | + System.out.println("is isBalanced : " + map.isBalanced()); |
| 338 | + |
| 339 | + for (String word : words) { |
| 340 | + map.remove(word); |
| 341 | + if (!map.isBST() || !map.isBalanced()) |
| 342 | + throw new RuntimeException("Error"); |
| 343 | + } |
| 344 | + } |
| 345 | + |
| 346 | + System.out.println(); |
| 347 | + } |
| 348 | +} |
0 commit comments