• JS实现平衡二叉树


      1       class TreeNode {
      2         constructor(key) {
      3           this.key = key;
      4           this.leftChild = null;
      5           this.rightChild = null;
      6           this.parent = null;
      7         }
      8         rightRotate() {
      9           const sonNode = this.leftChild;
     10           const grandsonNode = sonNode.rightChild;
     11           sonNode.rightChild = this;
     12           sonNode.parent = null;
     13           this.parent = sonNode;
     14           this.leftChild = null;
     15           if (grandsonNode) {
     16             grandsonNode.parent = this;
     17           }
     18           this.leftChild = grandsonNode;
     19           return sonNode;
     20         }
     21         leftRotate() {
     22           const sonNode = this.rightChild;
     23           const grandsonNode = sonNode.leftChild;
     24           sonNode.leftChild = this;
     25           sonNode.parent = null;
     26           if (grandsonNode) {
     27             grandsonNode.parent = this;
     28           }
     29           this.rightChild = grandsonNode;
     30           return sonNode;
     31         }
     32       }
     33 
     34       class AVLTree {
     35         constructor(keys) {
     36           this.root = new TreeNode(keys[0]);
     37           const restKeys = keys.slice(1);
     38           restKeys.forEach((value) => {
     39             this.insertNode(new TreeNode(value), this.root);
     40           });
     41         }
     42         insertNode(newNode, parent) {
     43           if (newNode.key > parent.key) {
     44             if (parent.rightChild === null) {
     45               parent.rightChild = newNode;
     46               newNode.parent = parent;
     47             } else {
     48               this.insertNode(newNode, parent.rightChild);
     49             }
     50           } else if (newNode.key < parent.key) {
     51             if (parent.leftChild === null) {
     52               parent.leftChild = newNode;
     53               newNode.parent = parent;
     54             } else {
     55               this.insertNode(newNode, parent.leftChild);
     56             }
     57           }
     58           //当添加完一个节点后,如果右子树的高度-左子树的高度>1,左旋转
     59           if (
     60             this.getHeight(parent.rightChild) -
     61               this.getHeight(parent.leftChild) >
     62             1
     63           ) {
     64             //如果它的右子树的左子树大于它的右子树的右子树的高度
     65             if (
     66               this.getHeight(parent.rightChild.leftChild) >
     67               this.getHeight(parent.rightChild.rightChild)
     68             ) {
     69               parent.rightChild = parent.rightChild.rightRotate();
     70             }
     71             //当前节点左旋转
     72             if (parent.parent) {
     73               parent.parent.rightChild = parent.leftRotate();
     74             } else {
     75               this.root = parent.leftRotate();
     76             }
     77             return;
     78           }
     79 
     80           //当添加完后,如果左子树的高度-右子树的高度>1,右旋转
     81           if (
     82             this.getHeight(parent.leftChild) -
     83               this.getHeight(parent.rightChild) >
     84             1
     85           ) {
     86             //如果它的左子树的右子树大于它的左子树的左子树的高度
     87             if (
     88               this.getHeight(parent.leftChild.rightChild) >
     89               this.getHeight(parent.leftChild.leftChild)
     90             ) {
     91               //先对其当前节点的左节点进行左旋转
     92               parent.leftChild = parent.leftChild.leftRotate();
     93             }
     94             //对当前节点进行右旋转
     95             if (parent.parent) {
     96               parent.parent.leftChild = parent.rightRotate();
     97             } else {
     98               this.root = parent.rightRotate();
     99             }
    100           }
    101         }
    102         getHeight(node) {
    103           if (!node) {
    104             return 0;
    105           } else {
    106             return (
    107               Math.max(
    108                 node.leftChild === null ? 0 : this.getHeight(node.leftChild),
    109                 node.rightChild === null ? 0 : this.getHeight(node.rightChild)
    110               ) + 1
    111             );
    112           }
    113         }
    114       }
    115 
    116       //测试数据
    117       // console.log(new AVLTree([1, 2, 3]));
    118       // console.log(new AVLTree([1, 2, 3, 4]));
    119       // console.log(new AVLTree([4, 3, 2, 1]));
    120       // console.log(new AVLTree([3, 2, 4, 5, 1, 0]));
    121       // console.log(new AVLTree([10, 11, 7, 6, 8, 9]));
    122       // console.log(new AVLTree([2, 1, 5, 3, 6, 4]));
    123       // console.log(new AVLTree([6, 2, 9, 1, 8, 12, 7, 11, 13, 10]));
    124 
    125       // console.log(new AVLTree([3, 2, 1]));
    126       // console.log(new AVLTree([1, 2, 3]));
    127       // console.log(new AVLTree([5, 2, 6, 1, 4, 3]));
    128       // console.log(new AVLTree([5, 2, 6, 1, 3, 4]));
    129       // console.log(new AVLTree([4, 2, 3]));
    130       // console.log(new AVLTree([2, 1, 5, 3, 6, 4]));
    131       // console.log(new AVLTree([2, 1, 5, 4, 6, 3]));
    132       // console.log(new AVLTree([2, 3, 4]));

    今天试着实现了一个平衡二叉树,平衡二叉树和二叉树其实只差平衡二字,但其实最难的便是这两步。导致不平衡的情况有很多,大家可以参考一下这篇博客。https://www.cnblogs.com/li-ningning/p/12358460.html

    博客中博主把导致不平衡的结点分成了八类,但我们是否需要为8种情况设置8种调整方法呢?事实上是不需要的。通过观察我们可以发现所有调整方法都可以总结为两种情况:

    1. 当前节点的左子树高度大于右子树,此时需要再判断当前结点左子结点,如果左子结点的右子树高度大于左子树高度,需要对左子结点先进行左旋转,然后再对当前的节点进行右旋转。
    2. 当前节点的右子树高度大于左子树,此时需要再判断当前结点右子结点,如果右子结点的左子树高度大于右子树高度,需要对右子结点先进行右旋转,然后再对当前的节点进行左旋转。

    写代码的时候有几个点必须要时刻注意,旋转是对于哪个结点来说的,同时如果旋转的结点是根节点还需要特别处理。在代码中使用了双指针,这样子节点和父节点就能实现互访。

    同时左右旋转的时候还需要判断某些特殊结点是否存在,即代码中的grandsonNode,这些结点在左右旋转后的变化都是比较大的。

    博主在写代码时候还发现一个问题,就是总觉得会有很多特殊的情况没有处理到,然后平衡处理逻辑越写越复杂。最后其实发现也不过就是上面两种方法的子集。这些情况也是上面博主引用那篇博客所述情况的子集。所以总结规律很重要,emmm,如果不擅长,那不如就记住吧,hhh。

    有问题的话欢迎在评论区与博主讨论哦。

  • 相关阅读:
    移动端开发rem布局之less+媒体查询布局的原理步骤和心得
    前端实现文件下载方式总汇
    如何能提高CSS编写技巧?提高Web前端开发效率
    常用的CSS命名规则
    CSS背景background
    CSS盒子模型
    简单的树形菜单如何写?
    彻底掌握css动画【transition】
    首页白屏优化实践
    我来聊聊面向模板的前端开发
  • 原文地址:https://www.cnblogs.com/AwenJS/p/13934030.html
Copyright © 2020-2023  润新知