自平衡树(AVL),是为了解决二叉搜素树存在的可能某一节点会嵌套太深的缺点。ASVL是一种自平衡二叉搜索树,左子树和右子树高度最多相差1。在添加或移除节点时,AVL树会尽可能尝试转换成完全树.
在AAVL中,需要对每个节点计算右子树高度和左子树高度之间的差值,该值(hr-hl)应为0,1,-1.如果结果不是这三个值之一,需要平衡树AVL树,这就是平衡因子的概念;

节点的高度

节点的高度是从最下开始获取

  • 最下的节点是0开始(后面总结:高度高度从0或者1开始都不影响计算)
  • 如果有多个分之,中间的高度是按照最高的高度计算获取。

    height.png

平衡因子

平衡因子只能是 0,1,-1 ,如果不是这三个值之一则就需要计算该AVL树;
平衡因子计算,有的说左高-右高,有的说右高-左高,不过不影响计算。下面按照 左高-右高计算获取平衡因子
每个节点都右自己的平衡因子 ,如下7,13也有自己平衡因子,只不过没有值所以平衡因子为0;
image.png

平衡操作 - AVL旋转

在对AVL树添加或移除操作后,需要计算节点的高度并验证树是否需要进行平衡。向AVL树插入如节点时,可以执行单旋转或双旋转两种平衡操作。

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

    左-左(LL):向右的单向旋转

    这种情况出现于节点的左侧节点的高度大于右侧节点的高度时候。
    解:因为第二层肯定比三层小,比一层大。所以直接把三层放到到二层右侧就行。
    image.png
    1. // LL 向左单旋转
    2. rotationLL(node) {
    3. const tmp = node.left;
    4. node.left = tmp.right;
    5. tmp.right = node;
    6. return tmp
    7. }

右-右(RR):向左的单向旋转

右侧子节点的高度大于左侧子节点的高度,并且右侧子节点也是平衡或右侧较重的。
image.png

  1. // RR 向右单旋转
  2. rotationRR(node) {
  3. const tmp = node.right;
  4. node.right = tmp.left;
  5. tmp.left = node;
  6. return tmp;
  7. }

左-右(LR):先右的双旋转

左侧子节点的高度大于右侧子节点的高度,并且左侧子节点右侧较重,这种情况对左侧子节点进行左旋转来修改,然后在对不平衡的节点来一个右旋转来修复
image.png

  1. // LR 向右的双旋转
  2. rotationLR(node) {
  3. node.left = this.rotationRR(node.left);
  4. return this.rotationLL(node)
  5. }

右-左(RL):向左的双旋转

右侧子节点的高度大于左侧子节点的高度,并且右侧子节点左侧较重,先进行右旋转,在通过左旋转修复
image.png

  1. // Rl 右左旋转
  2. rotationRL(node) {
  3. node.right = this.rotationLL(node.right);
  4. return this.rotationRR(node);
  5. }

插入节点

  1. insert(key) {
  2. this.root = this.insertNode(this.root, key);
  3. }
  4. // 插入节点 如果不在插入 存在直接返回 节点不平衡 旋转
  5. insertNode(node, key) {
  6. if (node === null) {
  7. // 没有数据的时候直接创建
  8. return new Node(key)
  9. } else if (this.defaultCompare(key, node.key) === -1) {
  10. // 插到左侧
  11. node.left = this.insertNode(node.left, key)
  12. } else if (this.defaultCompare(key, node.key) === 1) {
  13. // 插到右侧
  14. node.right = this.insertNode(node.right, key)
  15. } else {
  16. // 如果插入的节点存在 直接返回
  17. return node;
  18. }
  19. // 进行二叉树平衡操作
  20. const balanceFactor = this.getBalanceFactor(node);
  21. // 左侧树插入节点后不平衡情况下
  22. if (balanceFactor === BalanceFactor.UNBALANCED_LEFT /* 5 */) {
  23. // 插入值比左侧小
  24. if (this.defaultCompare(key, node.left.key) === -1) {
  25. node = this.rotationLL(node)
  26. } else {
  27. return this.rotationLR(node);
  28. }
  29. }
  30. // 右侧树插入节点后不平衡情况下
  31. if (balanceFactor === BalanceFactor.UNBALANCED_RIGHT /* 1 */) {
  32. // 插入值比右侧小
  33. if (this.defaultCompare(key, node.right.key) === 1) {
  34. debugger
  35. node = this.rotationRR(node);
  36. } else {
  37. return this.rotationRL(node)
  38. }
  39. }
  40. return node;
  41. }

移除节点

  1. remove(key){
  2. return this.removeNode(this.root,key)
  3. }
  4. // 移除节点
  5. removeNode(node, key) {
  6. // 使用继承方法的移除; 递归移除
  7. node = super.removeNode(node, key);
  8. if (node === null) return node // null 不需要平衡
  9. // 检测树是否平衡 递归计算 以移除的节点 为根节点的平衡因子
  10. const balanceFactor = this.getBalanceFactor(node);
  11. // 左侧移除完后不平衡
  12. if (balanceFactor === BalanceFactor.UNBALANCED_LEFT /* 5 */) {
  13. // 计算左侧树平衡因子
  14. const balanceFactorLeft = this.getBalanceFactor(node.left);
  15. // 左侧树先左不平衡
  16. if (
  17. balanceFactorLeft === BalanceFactor.BALANCED || // 3
  18. balanceFactorLeft === BalanceFactor.SLIGHTLY_UNBALANCED_LEFT // 4
  19. ) {
  20. // 左侧旋转
  21. return this.rotationLL(node)
  22. }
  23. // 左侧树向右不平衡
  24. if (balanceFactorLeft === BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT/* 2 */) {
  25. // 旋转
  26. return this.rotationLR(node);
  27. }
  28. }
  29. // 右侧树移除完后不平衡
  30. if (balanceFactor === BalanceFactor.UNBALANCED_RIGHT/* 1 */) {
  31. // 计算右侧平衡因子
  32. const balanceFactorRight = this.getBalanceFactor(node.right);
  33. // 右侧树向右不平衡
  34. if (
  35. balanceFactorRight === BalanceFactor.BALANCED || // 3
  36. balanceFactorRight === BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT // 2
  37. ) {
  38. // 有侧旋转
  39. return this.rotationRR(node)
  40. }
  41. // 右侧向左不平衡
  42. if (balanceFactorRight === BalanceFactor.SLIGHTLY_UNBALANCED_LEFT/* 4 */) {
  43. // 左右旋转
  44. return this.rotationRL(node.right)
  45. }
  46. }
  47. return node;
  48. }

https://github.com/qdwds/data-structure