概念

BST 存在一个问题:
取决于你添加的节点数,树的一条边可能会非常深;
也就是说,树的一条分支会有很多层,而其他的分支却只有几层,
这会造成很大的性能问题
所以需要平衡二叉搜索树来解决这个问题
平衡二叉树 - 图1
平衡二叉树的概念

  • 在AVL 树中,需要对每个节点计算右子树高度(hr)和左子树高度(hl)之间的差值,该值(hr-hl)应为0、1 或 -1
  • 如果结果不是这三个值之一,则需要平衡该AVL 树。
    这就是平衡因子的概念。
    平衡二叉树 - 图2

AVL的实现

  • 我们可以扩展我们写的BST 类,只需要覆盖用来维持AVL 树平衡的方法,也就是insert、insertNode 和removeNode 方法。
  • 所有其他的BST 方法将会被AVLTree 类继承。

1 平衡因子的计算

  1. const BalanceFactor = {
  2. UNBALANCED_RIGHT: 1,
  3. SLIGHTLY_UNBALANCED_RIGHT: 2,
  4. BALANCED: 3,
  5. SLIGHTLY_UNBALANCED_LEFT: 4,
  6. UNBALANCED_LEFT: 5
  7. };
  1. // 树的高度
  2. getNodeHeight(node) {
  3. if (!node) {
  4. return -1;
  5. }
  6. return Math.max(this.getNodeHeight(node.left), this.getNodeHeight(node.right)) + 1;
  7. }
  8. // 两个树的高度差
  9. getBalanceFactory(node) {
  10. let heightDifference = this.getNodeHeight(node.left) - this.getNodeHeight(node.right);
  11. switch (heightDifference) {
  12. case -2: // 右边不平衡
  13. return BalanceFactory.UNBALANCE_RIGHT;
  14. case -1: // 右边有点点不平衡
  15. return BalanceFactory.SLIGHTLY_UNBALANCE_RIGHT;
  16. case 0: // 平衡
  17. return BalanceFactory.BALANCE;
  18. case 1: // 左边有点点不平衡
  19. return BalanceFactory.SLIGHTLY_UNBALANCE_LEFT;
  20. case 2: // 左边不平衡
  21. return BalanceFactory.UNBALANCE_LEFT;
  22. }
  23. }

2 平衡操作

  • 左-左(LL):向右的单旋转
  • 右-右(RR):向左的单旋转
  • 左-右(LR):向右的双旋转(先LL 旋转,再RR 旋转)
  • 右-左(RL):向左的双旋转(先RR 旋转,再LL 旋转)

向右的单旋转LL

这种情况出现于节点的左侧子节点的高度大于右侧子节点的高度时,并且左侧子节点也是平衡或左侧较重的,这个时候需要左子树放在上面。者就是所谓的右单旋转
平衡二叉树 - 图3
假设向AVL 树插入节点5,这会造成树失衡(节点50-Y 高度为+2),需要恢复树的平衡。

  1. 保存不平衡树node的左子树temp
  2. node左子树替换为temp的右子树
  3. temp的右子树替换为改变后的node树

平衡二叉树 - 图4
平衡二叉树 - 图5
平衡二叉树 - 图6

  1. rotationLL(node) {
  2. let temp = node.left;
  3. node.left = temp.right;
  4. temp.right = node;
  5. return temp;
  6. }

平衡二叉树 - 图7

RR:向左的单旋转

与LL情况相反。它出现于右侧子节点的高度大于左侧子节点的高度,并且右侧子节点也是平衡或右侧较重的

  1. rotationRR(node) {
  2. let temp = node.right;
  3. node.right = temp.left;
  4. temp.left = node;
  5. return temp;
  6. }

向右的双旋转LR:Left-Right

  • 这种情况出现于左侧子节点的高度大于右侧子节点的高度,并且左侧子节点右侧较重.
  • 简而言之,左子树重了,左子树中的右子树也重了
  • 在这种情况下,可以先对左侧子节点进行RR左旋转来修复,这样会形成LL的情况,然后再对不平衡的节点进行一个LL右旋转来修复

平衡二叉树 - 图8

  1. rotationLR(node) {
  2. node.left = this.rotationRR(node.left);
  3. return this.rotationLL(node);
  4. }

向左的双旋转RL: Right-Left

  1. 先对右子树进行LL右旋,整个树形成RR条件
  2. 再对整个树RR左旋
    平衡二叉树 - 图9
  1. rotationRL(node) {
  2. node.right = this.rotationLL(node.right);
  3. return this.rotationRR(node);
  4. }

3 插入结点

插入方法和bst是一样的,不过还需要讨论不平衡的情况

  1. 如果在向左侧子树插入节点后树不平衡了,我们需要比较是否插入的键小于左侧子节点的键。如果是,我们要进行LL 旋转。否则,要进行LR 旋转。
  2. 如果在向右侧子树插入节点后树不平衡了,我们需要比较是否插入的键小于右侧子节点的键。如果是,我们要进行RR 旋转。否则,要进行RL 旋转。
  1. insert(key) {
  2. this.root = this.insertNode(this.root, key);
  3. }
  4. insertNode(node, key) {
  5. // 像在BST 树中一样插入节点
  6. if (node == null) {
  7. return new Node(key);
  8. } else if (this.compareFn(key, node.key) === Compare.LESS_THAN) {
  9. node.left = this.insertNode(node.left, key);
  10. } else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) {
  11. node.right = this.insertNode(node.right, key);
  12. } else {
  13. return node; // 重复的键
  14. }
  15. // 如果需要,将树进行平衡操作
  16. const balanceFactor = this.getBalanceFactor(node);
  17. // 左不平衡
  18. if (balanceFactor === BalanceFactor.UNBALANCED_LEFT) {
  19. // 判断是否存在LR情况
  20. if (this.compareFn(key, node.left.key) === Compare.LESS_THAN) {
  21. node = this.rotationLL(node);
  22. } else {
  23. return this.rotationLR(node);
  24. }
  25. }
  26. // 右不平衡
  27. if (balanceFactor === BalanceFactor.UNBALANCED_RIGHT) {
  28. if (this.compareFn(key, node.right.key) === Compare.BIGGER_THAN) {
  29. node = this.rotationRR(node);
  30. } else {
  31. return this.rotationRL(node);
  32. }
  33. }
  34. return node;
  35. }

4 删除结点

  1. removeNode(node, key) {
  2. node = super.removeNode(node, key);
  3. if (node == null) {
  4. return node; // null,不需要进行平衡
  5. }
  6. // 检测树是否平衡
  7. const balanceFactor = this.getBalanceFactor(node);
  8. // 左不平衡
  9. if (balanceFactor === BalanceFactor.UNBALANCED_LEFT) {
  10. // 左子树状态
  11. const balanceFactorLeft = this.getBalanceFactor(node.left);
  12. // 如果是平衡(h = 0)或者有点不平衡(h = 1),LL
  13. if (balanceFactorLeft === BalanceFactor.BALANCED ||
  14. balanceFactorLeft === BalanceFactor.SLIGHTLY_UNBALANCED_LEFT) {
  15. return this.rotationLL(node);
  16. }
  17. // 如果不平衡(h = 2)LR
  18. if (balanceFactorLeft === BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT) {
  19. return this.rotationLR(node.left);
  20. }
  21. }
  22. // 右不平衡
  23. if (balanceFactor === BalanceFactor.UNBALANCED_RIGHT) {
  24. // 右子树
  25. const balanceFactorRight = this.getBalanceFactor(node.right);
  26. if (balanceFactorRight === BalanceFactor.BALANCED ||
  27. balanceFactorRight === BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT) {
  28. return this.rotationRR(node);
  29. }
  30. if (balanceFactorRight === BalanceFactor.SLIGHTLY_UNBALANCED_LEFT) {
  31. return this.rotationRL(node.right);
  32. }
  33. }
  34. return node;
  35. }

完整代码

  1. class Node {
  2. constructor(key) {
  3. this.key = key; // 节点值
  4. this.left = null; // 左侧子节点引用
  5. this.right = null; // 右侧子节点引用
  6. }
  7. }
  8. const Compare = {
  9. LESS_THAN: -1,
  10. BIGGER_THAN: 1
  11. };
  12. class BinarySearchTree {
  13. constructor() {
  14. this.root = null; // Node 类型的根节点 默认就是null
  15. }
  16. compareFn(a, b) {
  17. if (a === b) { //
  18. return 0;
  19. }
  20. return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN; // 比较函数, a<b返回-1. 大于则返回正1
  21. }
  22. insert(key) {
  23. if (this.root == null) {
  24. this.root = new Node(key);
  25. } else {
  26. this.insertNode(this.root, key);
  27. }
  28. }
  29. insertNode(node, key) {
  30. if (this.compareFn(key, node.key) === Compare.LESS_THAN) {
  31. if (node.left == null) {
  32. node.left = new Node(key);
  33. } else {
  34. this.insertNode(node.left, key);
  35. }
  36. } else {
  37. if (node.right == null) {
  38. node.right = new Node(key);
  39. } else {
  40. this.insertNode(node.right, key);
  41. }
  42. }
  43. }
  44. inOrderTraverse(callback) {
  45. this.inOrderTraverseNode(this.root, callback);
  46. }
  47. inOrderTraverseNode(node, callback) {
  48. if (node != null) {
  49. this.inOrderTraverseNode(node.left, callback);
  50. callback(node.key);
  51. this.inOrderTraverseNode(node.right, callback);
  52. }
  53. }
  54. preOrderTraverse(callback) {
  55. this.preOrderTraverseNode(this.root, callback);
  56. }
  57. preOrderTraverseNode(node, callback) {
  58. if (node != null) {
  59. callback(node.key);
  60. this.preOrderTraverseNode(node.left, callback);
  61. this.preOrderTraverseNode(node.right, callback);
  62. }
  63. }
  64. postOrderTraverse(callback) {
  65. this.postOrderTraverseNode(this.root, callback);
  66. }
  67. postOrderTraverseNode(node, callback) {
  68. if (node != null) {
  69. this.postOrderTraverseNode(node.left, callback);
  70. this.postOrderTraverseNode(node.right, callback);
  71. callback(node.key);
  72. }
  73. }
  74. min() {
  75. return this.minNode(this.root);
  76. }
  77. minNode(node) {
  78. let current = node;
  79. while (current != null && current.left != null) {
  80. current = current.left;
  81. }
  82. return current;
  83. }
  84. max() {
  85. return this.maxNode(this.root);
  86. }
  87. maxNode(node) {
  88. let current = node;
  89. while (current != null && current.right != null) {
  90. current = current.right;
  91. }
  92. return current;
  93. }
  94. search(key) {
  95. return this.searchNode(this.root, key);
  96. }
  97. searchNode(node, key) {
  98. if (node == null) {
  99. return false;
  100. }
  101. if (this.compareFn(key, node.key) === Compare.LESS_THAN) {
  102. return this.searchNode(node.left, key);
  103. } else if (
  104. this.compareFn(key, node.key) === Compare.BIGGER_THAN
  105. ) {
  106. return this.searchNode(node.right, key);
  107. } else {
  108. return true;
  109. }
  110. }
  111. remove(key) {
  112. this.root = this.removeNode(this.root, key);
  113. }
  114. removeNode(node, key) {
  115. if (node == null) {
  116. return null;
  117. }
  118. if (this.compareFn(key, node.key) === Compare.LESS_THAN) {
  119. node.left = this.removeNode(node.left, key);
  120. return node;
  121. } else if (
  122. this.compareFn(key, node.key) === Compare.BIGGER_THAN
  123. ) {
  124. node.right = this.removeNode(node.right, key);
  125. return node;
  126. } else {
  127. // 键等于node.key
  128. // 第一种情况
  129. if (node.left == null && node.right == null) {
  130. node = null;
  131. return node;
  132. }
  133. // 第二种情况
  134. if (node.left == null) {
  135. node = node.right;
  136. return node;
  137. } else if (node.right == null) {
  138. node = node.left;
  139. return node;
  140. }
  141. // 第三种情况
  142. const aux = this.minNode(node.right);
  143. node.key = aux.key;
  144. node.right = this.removeNode(node.right, aux.key);
  145. return node;
  146. }
  147. }
  148. }
  149. const BalanceFactor = {
  150. UNBALANCED_RIGHT: 1,
  151. SLIGHTLY_UNBALANCED_RIGHT: 2,
  152. BALANCED: 3,
  153. SLIGHTLY_UNBALANCED_LEFT: 4,
  154. UNBALANCED_LEFT: 5
  155. };
  156. class AVLTree extends BinarySearchTree {
  157. constructor() {
  158. super();
  159. this.root = null;
  160. }
  161. getNodeHeight(node) {
  162. if (node == null) {
  163. return -1;
  164. }
  165. return Math.max(
  166. this.getNodeHeight(node.left), this.getNodeHeight(node.right)
  167. ) + 1;
  168. }
  169. getBalanceFactor(node) {
  170. const heightDifference = this.getNodeHeight(node.left) -
  171. this.getNodeHeight(node.right);
  172. switch (heightDifference) {
  173. case -2:
  174. return BalanceFactor.UNBALANCED_RIGHT;
  175. case -1:
  176. return BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT;
  177. case 1:
  178. return BalanceFactor.SLIGHTLY_UNBALANCED_LEFT;
  179. case 2:
  180. return BalanceFactor.UNBALANCED_LEFT;
  181. default:
  182. return BalanceFactor.BALANCED;
  183. }
  184. }
  185. rotationLL(node) {
  186. const tmp = node.left;
  187. node.left = tmp.right;
  188. tmp.right = node;
  189. return tmp;
  190. }
  191. rotationRR(node) {
  192. const tmp = node.right;
  193. node.right = tmp.left;
  194. tmp.left = node;
  195. return tmp;
  196. }
  197. rotationLR(node) {
  198. node.left = this.rotationRR(node.left);
  199. return this.rotationLL(node);
  200. }
  201. rotationRL(node) {
  202. node.right = this.rotationLL(node.right);
  203. return this.rotationRR(node);
  204. }
  205. insert(key) {
  206. this.root = this.insertNode(this.root, key);
  207. }
  208. insertNode(node, key) {
  209. // 像在BST 树中一样插入节点
  210. if (node == null) {
  211. return new Node(key);
  212. } else if (this.compareFn(key, node.key) === Compare.LESS_THAN) {
  213. node.left = this.insertNode(node.left, key);
  214. } else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) {
  215. node.right = this.insertNode(node.right, key);
  216. } else {
  217. return node; // 重复的键
  218. }
  219. // 如果需要,将树进行平衡操作
  220. const balanceFactor = this.getBalanceFactor(node);
  221. if (balanceFactor === BalanceFactor.UNBALANCED_LEFT) {
  222. if (this.compareFn(key, node.left.key) === Compare.LESS_THAN) {
  223. node = this.rotationLL(node);
  224. } else {
  225. return this.rotationLR(node);
  226. }
  227. }
  228. if (balanceFactor === BalanceFactor.UNBALANCED_RIGHT) {
  229. if (
  230. this.compareFn(key, node.right.key) === Compare.BIGGER_THAN
  231. ) {
  232. node = this.rotationRR(node);
  233. } else {
  234. return this.rotationRL(node);
  235. }
  236. }
  237. return node;
  238. }
  239. removeNode(node, key) {
  240. node = super.removeNode(node, key);
  241. if (node == null) {
  242. return node; // null,不需要进行平衡
  243. }
  244. // 检测树是否平衡
  245. const balanceFactor = this.getBalanceFactor(node);
  246. // 左不平衡
  247. if (balanceFactor === BalanceFactor.UNBALANCED_LEFT) {
  248. // 左子树状态
  249. const balanceFactorLeft = this.getBalanceFactor(node.left);
  250. // 如果是平衡(h = 0)或者有点不平衡(h = 1),LL
  251. if (balanceFactorLeft === BalanceFactor.BALANCED ||
  252. balanceFactorLeft === BalanceFactor.SLIGHTLY_UNBALANCED_LEFT) {
  253. return this.rotationLL(node);
  254. }
  255. // 如果不平衡(h = 2)LR
  256. if (balanceFactorLeft === BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT) {
  257. return this.rotationLR(node.left);
  258. }
  259. }
  260. // 右不平衡
  261. if (balanceFactor === BalanceFactor.UNBALANCED_RIGHT) {
  262. // 右子树
  263. const balanceFactorRight = this.getBalanceFactor(node.right);
  264. if (balanceFactorRight === BalanceFactor.BALANCED ||
  265. balanceFactorRight === BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT) {
  266. return this.rotationRR(node);
  267. }
  268. if (balanceFactorRight === BalanceFactor.SLIGHTLY_UNBALANCED_LEFT) {
  269. return this.rotationRL(node.right);
  270. }
  271. }
  272. return node;
  273. }
  274. }
  275. let avl = new AVLTree();
  276. avl.insert(70);
  277. avl.insert(50);
  278. avl.insert(80);
  279. avl.insert(72);
  280. avl.insert(90);
  281. avl.insert(75);
  282. avl.remove(70);
  283. avl.remove(50);
  284. console.log(avl);