Use js to implement those data structures

A problem that arises when using a binary search tree is that one branch of the tree has many levels, while the other branches have only a few levels, like this:

  If the amount of data is large enough, it will consume a lot of time when we perform addition, deletion, modification, and query operations on an edge. We expended energy to construct a structure that would increase efficiency, and it backfired. This is not what we want. Therefore, we need another kind of tree to solve such a problem, that is, the self-balancing binary search tree - Adelson-Velskii-Landi (AVL). What does that mean? That is to say, the difference between the heights of the left and right subtrees of any node of this tree is at most 1. That is, the tree tries to be a complete tree as much as possible when adding or removing nodes.

  The implementation of self-balancing binary search tree and binary search tree is almost the same, the only difference is that every time we insert or delete a node, we need to check its balance factor (because it is only when inserting or deleting again). may affect the balance of the tree) . If necessary, then apply its logic to the self-balancing of the tree.

  First we need to know how this balance factor is calculated. The balance factor is calculated from the difference between the height of the right subtree (hr) and the height of the left subtree (hl) for each node, which should be 0, 1, -1. If it is not these three values, then explain This AVL tree needs to be balanced. This is how the balance factor is simply calculated. What does that mean?

   Taking the above figure as an example, the balance factor of root node 11 is 6 - 3 = 3. The balance factor of the left child node 7 is 2 - 2 = 0; the balance factor of the right child node 18 is 5 - 2 = 3; the balance factor of node 70 is 0, remember that all leaf nodes (external nodes) have The balance factor is all 0. Because leaf nodes have no children. There is one more thing to note. The balance factor we calculate is the height of the left and right subtrees of the node.

  We've learned how to calculate the balance factor, so let's do a very important ritual... oh, sorry. Yes and the most important knowledge - rotation.

  Before we start explaining the spins, let's have some appetizers. Let's see what are the possible situations that can cause the tree to be unbalanced after we insert child nodes. I will draw a few pictures so that everyone can see it carefully and thoroughly.

  

 

  First of all, we use the above picture (cutting a part of the previous tree structure) as the initial tree, this tree must be absolutely balanced. Everyone has no opinion. So what do RR, LL, RL, LR mean? Then we continue to look down.

  The first case: RR.

    We add a node 20 to the right child node of 18, and the right side is to add a value larger than the parent node.

 

 

    After we added a node 20, we found that the tree was still balanced! Ugh? No, it doesn't seem like the result I wanted. I add another node to try?

 

 

    Well...now it's absolutely unbalanced. So let's see what happened. After adding node 21 (19), the depth of the left subtree of node 11 is 1, and the depth of the right subtree is 1, 2, 3. 3 is right. Then 1-3 equals -2. Well, addition and subtraction within ten should not be wrong. We determined that after adding two right (R) nodes after node 18, the tree was unbalanced. And now there is an important question. Which subtree is causing the tree to be unbalanced? After we added node 21 (19), which is the part of the picture above where we cursed it with small circles. Then we can describe it in one sentence. After we add a right child node to the right child node of the right child node of the tree (the same is true if the left child node is added) , it leads to the inconsistency of the tree. Balance, so we need to operate at this time, that is, rotate the child node on the right, which is 18 nodes, to make this self-balancing tree come from balance. In other words, if we add a node (or delete a node) that causes our entire tree to be unbalanced, then we first need to find the nearest unbalanced tree to adjust.

    In the case of RR above, I added two byte points, 19 and 21 respectively. To explain, these two sub-nodes are to tell you more clearly that under the right node of the root's right node, no matter the left node is inserted Or the case where both right nodes belong to RR . The same below. In the specific rotation will give you a detailed introduction.

    换句话说,我们判断在增删节点的时候是否会导致不平衡的情况,由插入节点的前两个父节点来确定!大家要注意噢!很重要!

    趁热打铁,上面解释了RR的情况,那么其实下面的LL,LR,RL等情况也是一样的。思路没有任何区别。但是这个时候我想打断大家一下。问大家两个问题。这两个问题的解决会为后面的学习带来极大的便利。

    1、在AVL树或者其他树中,是否可以出现重复的值,比如树中已经有了一个11,我还想再加入一个11,是否允许?是否可以?

    2、看上图(RR情况图),是否有可能出现除了这四种情况外的其他情况?或者说,节点的平衡因子是否可能出现大于2或者小于-2的情况?(这种情况我们要旋转树超过两次,也就超出了我们这四种情况之外。)

    OK。希望大家闭上眼睛,想一想你的梦中情人,哦不对。想一想你的答案。

    不卖关子了,但是我真的希望大家想一想,因为这很必要也很重要。

    好吧,我开始回答第一个问题。其实在前一篇实现的树中是不允许重复的值出现的,我们可以去看一下上一篇的代码,如果相等则会覆盖。那么可能有人会问,我想要这棵树存储重复的值(当然其实这种情况出现的话大多数都是你的设计有问题。。。没有唯一标识了啊......需求还怎么实现)。那么我记得在hashMap篇中有一个解决冲突的办法,是不是可以通过链表来存储key值相同的映射呢?是否还可以使用其他的存储方式?答案比较开放。所以是否可以存放重复的值,看你的实际需求咯。

    第二个问题的答案,不可能出现,因为大家一定要记住一个前提,就是我们在插入了一个导致该树不平衡的节点前,该树一定是平衡的。为什么这么说呢?因为我们的AVL树,是自平衡二叉搜索树,如果在插入之前就是不平衡的,那你告诉我你这是啥?赶紧回头看代码,有BUG了亲。

    这里希望大家已经解除了心中不少的疑惑,如果还有问题,大家可以继续留言探讨。

    那么我们下面继续,把其它几种情况的图示画完。

  第二种情况:LL。

 

  第三种情况:LR。

 

 

  第四种情况:RL。

 

   那么看完上面这几幅图想必大家都了解了在插入节点的时候影响到树的平衡的4种可能性。那么为了面对这4种可能性。我们给出了与之相对应的4种解决不平衡的方法(其实就两种)。那么这里我们就要进入本篇最重要的内容了,旋转。在开始之前,希望大家记住一句话。那就是,什么情况导致的不平衡,那就用相反方向的旋转。什么意思呢,比如是LL导致的不平衡,那么我们就向右旋转。如果是RR导致的不平衡我们就向左,如果是RL,我们就LL再RR。如果是LR,我们就先RR再LL。好了,下面我们来看看究竟是如何旋转的吧。

   那么下面就有点意思了。希望大家可以仔细看。

一、RR情况的左旋转

  我们还得来看图说话,我尽量把图画的让人容易误解,哦不,容易理解。

  本人那个,画图工具用得还不是太熟练,拐歪的曲线没画出来,我拿嘴说吧......

  大家看上图,左旋是以18为轴心整个树的左部分向左旋转,这样就使18变成了根节点,11变成了18的左侧子节点。这样旋转一下,就相当于减少了一层右侧字树的一层深度,从而使整颗树变成了平衡树。那么可能还有下面的这种情况,但其实是一样的。

  那么这种情况是要旋转的轴心节点(18),还有左侧子节点,在旋转之后,18的左侧子节点13就会变成11的右侧子节点。其实可以简单的认为是左旋过后被节点11给“”过来的。

  其实,18的左侧子节点在旋转过后会成为11的右侧子节点还有一个原因,就是,18左侧子节点的值一定是大于11小于18的(旋转之前的图)。为什么自己想。那么在旋转过后,它也一定是大于11的,所以它可以成为11的右侧子节点。

一、LL情况的右旋转

  那么LL情况的右旋转就没什么好说的了,跟RR情况是一样的,我们直接上图吧。

  这绝壁没问题吧,原理都是一样的。只不过换了一个方向而已。

  这样没啥好说的了,对吧。下面我们看看其他地情况。双旋转......

三、LR情况的左旋(RR)右旋(LL)

  我们还是直接上图,然后再解释,解释完这个RL情况的又不用再啰嗦了。挺好......挺省事,嘿嘿。

  其实让人有点懵逼的是名字,我特意加了个括号,希望你别懵逼。

  不知道大家看没看懂,总感觉这图不是很友好啊,还有8节点的小瑕疵就不要在意了,反正都是虚线......还有指向节点10的那条线是虚线.....不影响.....嘿嘿。

  解释一下,我们需要双旋转的情况下,第一次旋转的是红框部分,也就是说,如果我们需要双旋转,两次旋转的轴心点是不一样的,第一次旋转的轴心是插入节点的父节点,而第二次旋转的轴心是插入节点的祖父节点大家一定要注意。

  那么这里可能会有一个疑问,就是8节点在第一次旋转过后,为什么会成为7节点的右侧子节点。这里十分重要,直接关系到你是否理解了AVL树的旋转。

  我们先看第一次旋转,如果插入的是8节点而不是10节点,那么在第一次左旋的时候,节点7会成为节点9的左侧子节点,而这个时候8节点是无处可去的,因为7占了我的位置,这咋整,不能因为一次平衡就删除我这个节点啊,节点8肯定不干,不然你插入我干啥.....哎?感觉有点不对劲.....额咳咳....咱们继续吧....而节点8这个位置一定比9小比7大,所以我们在旋转过后,让它成为7节点的右子节点就可以了。希望我说明白了。

  那么这个时候可能还存在7节点有左侧子节点的情况,上面没画,没关系啊,你是7节点的左侧子节点,左旋转过后你还在原来的位置,没人占你的位置,你就不用动了。嗯,就这样.....完毕!

四、RL情况的右旋(LL)旋(RR)

  其实这里真没啥好说的了,我一点都不解释,大家自己看,看不懂你就从头看!

  唉.......说了一大堆,终于可以到最后的代码了,上代码!

 

copy code
//这是我们计算当前节点的高度的方法
    var heightNode = function (node) {
        // 如果没有那就为-1
        if(node === null) {
            return -1;
        } else {
            // 如果存在执行逻辑
            //那么说一下这里我的理解吧,Math.max比较左节点和右节点的大小,返回大的那个值,然后 + 1。
            //为什么要返回大的那个值呢?因为如果左节点存在,那么值为0(-1 + 1);并且右节点是不存在的,那么右节点为-1。
            //但是此时我们是有高度的,所以我们要选取有高度的那个节点,也就是值大的那一个。
            //那为什么要+1呢?因为高度只能为0不能为-1。-1是我们通过相减计算得到的,而不是计算高度得到的。记住这里是计算高度。
            console.log(Math.max(heightNode(node.left),heightNode(node.right)) + 1)
            return Math.max(heightNode(node.left),heightNode(node.right)) + 1;
        }
    }

    //RR:向左的单旋转
    var rotationRR = function (node) {
        var tmp = node.right;
        node.right = tmp.left;
        tmp.left = node;
        return tmp;
    }

    //LL:向右的单旋转
    var rotationLL = function (node) {
        var tmp = node.left;
        node.left = tmp.right;
        tmp.right = node;
        return tmp;
    }

    //LR:向右得到双旋转
    var rotationLR = function (node) {
        node.left = rotationRR(node.left);
        return rotationLL(node);
    }

    //RL:向左的双旋转
    var rotationRL = function (node) {
        node.right = rorarionLL(node.right);
        return rorarionRR(node);
    }

    var balanceInsertNode = function (node,element) {
        // 如果node的位置没有值,那么直接加入就好了。
        if(node === null) {
            node = newNode(element);
        // 如果假如的值是小于当前节点的话,说明我们要加在当前节点的左侧。
        } else if(element < node.key) {
            node.left = insertNode(node.left,element);
            // 那么下面就要判断是否是null,如果是null,那么没问题,直接加上就好了。
            if(node.left !== null) {
                // 如果不是,我们就要计算node的左侧高度减去右侧高度是否大于1,如果是,说明不平衡,需要来调用平衡方法来平衡。
                if((heightNode(node.left) - heightNode(node.right)) > 1) {
                    // 如果当前插入的节点的值小于node.left的值,说明是LL的情况,我们需要右旋。否则的话我们就需要先左旋,再右旋。
                    if(element < node.left.key) {
                        node = rorarionLL(node);
                    } else {
                        node = rorarionLR(node);
                    }
                }
            }
        } else if(element > node.key) {
            node.right = insertNode(node.right,element);

            if(node.right !== null) {
                if((heightNode(node.right) - heightNode(node.left)) > 1) {
                    if(element > node.right.key) {
                        node = rorarionRR(node);
                    } else {
                        node = rorarionRL(node);
                    }
} } } }
copy code

   代码中多了一个balanceInsertNode方法,这个方法是需要替换我们前面写好的insertNode方法的,这样写是为了让大家更好的对比下。这些代码不像以前那样,写了一大堆的注释用来解释。其实要说的很多,都在前面的图和语言描述中说过了。所以大家看这个代码的时候。有不明白的地方,对照着前面的逻辑一点一点看,肯定就看明白了。比如rotationLL和rotationRR内部的替换以及为什么要这样替换,都在前面说过了。所以就不再在代码中啰嗦了。

http://news.xua4102.cn/
http://news.tzn6024.cn/
http://news.kme4313.cn/
http://news.bnb6875.cn/
http://news.yio4898.cn/
http://news.yat8046.cn/
http://news.mtl1611.cn/
http://news.ltz6047.cn/
http://news.wsa2392.cn/
http://news.xfz1235.cn/
http://news.fsh3334.cn/
http://news.hmb6894.cn/
http://news.zis3531.cn/
http://news.cyj2776.cn/
http://news.bpj4889.cn/
http://news.dvu3043.cn/
http://news.vrc3090.cn/
http://news.ecx0415.cn/
http://news.oyt3985.cn/
http://news.mfu9569.cn/
http://news.lev2249.cn/
http://news.bdw7316.cn/
http://news.yyf8629.cn/
http://news.baq6972.cn/
http://news.xcd5039.cn/
http://news.bed0568.cn/
http://news.hzg6462.cn/
http://news.dec9975.cn/
http://news.hyt6211.cn/
http://news.ysz1764.cn/
http://news.xah7645.cn/
http://news.rlo9176.cn/
http://news.iaz8522.cn/
http://news.mng2781.cn/
http://news.axs9870.cn/
http://news.csv7317.cn/
http://news.kpq2047.cn/
http://news.igm8568.cn/
http://news.cgn5379.cn/
http://news.axz7045.cn/
http://news.rfz4575.cn/
http://news.cxb4532.cn/
http://news.qri2046.cn/
http://news.zps7191.cn/
http://news.zcl0267.cn/
http://news.ozn1702.cn/
http://news.zbb7727.cn/
http://news.vtl3405.cn/
http://news.zht3189.cn/
http://news.zzl7747.cn/
http://news.abd5921.cn/
http://news.hch9349.cn/
http://news.hwp3498.cn/
http://news.wll1115.cn/
http://news.rol3427.cn/
http://news.akb6775.cn/
http://news.giy4971.cn/
http://news.tyo9948.cn/

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326792658&siteId=291194637