Use js to implement those data structures 14 (tree 02-AVL tree)

  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.

    In other words, we judge whether it will cause an imbalance when adding or deleting nodes, which is determined by the first two parent nodes of the inserted node! Everyone should pay attention! Very important!

    Strike while the iron is hot, the situation of RR is explained above, then the situation of LL, LR, RL, etc. below is the same. There is no difference in thinking. But at this time I want to interrupt everyone. Ask you two questions. The solution of these two problems will bring great convenience to the later study.

    1. In the AVL tree or other trees, can duplicate values ​​appear, for example, there is already an 11 in the tree, and I want to add another 11, is it allowed? Is it possible?

    2. Looking at the above picture (RR situation picture), is there any other situation other than these four situations? In other words, is it possible that the balance factor of the node is greater than 2 or less than -2? (In this case we have to rotate the tree more than twice, which is beyond our four cases.)

    OK. I hope you all close your eyes and think about the person of your dreams, oh no. Think about your answer.

    It's not a joke, but I really want you to think about it, because it's necessary and important.

    Well, I start to answer the first question. In fact, duplicate values ​​are not allowed in the tree implemented in the previous article. We can look at the code in the previous article. If they are equal, they will be overwritten. Then some people may ask, I want this tree to store duplicate values ​​(of course, in fact, most of the cases in this case are due to your design problems... there is no unique identifier... What is the requirement? accomplish). Then I remember that there is a way to resolve conflicts in the hashMap chapter . Is it possible to store maps with the same key value through a linked list? Are there other storage methods that can be used? The answer is more open. So whether you can store duplicate values ​​depends on your actual needs.

    The answer to the second question is impossible, because everyone must remember a premise that before we insert a node that causes the tree to be unbalanced, the tree must be balanced. Why do you say that? Because our AVL tree is a self-balancing binary search tree, if it is unbalanced before insertion, then tell me what you are? Look back at the code quickly, there is a bug.

    I hope everyone here has cleared a lot of doubts in their hearts. If you still have questions, you can continue to leave a message to discuss.

    So let's continue to draw the diagrams of the other situations.

  Second case: LL.

 

  The third case: LR.

 

 

  Fourth case: RL.

 

   Then after reading the above pictures, everyone must have understood the four possibilities that affect the balance of the tree when inserting nodes. So in order to face these 4 possibilities. We give the corresponding 4 methods to solve the imbalance (actually there are two). So here we are going to enter the most important content of this article, rotation . Before we start, I hope you all remember one sentence. That is, if the situation causes the imbalance, then use the rotation in the opposite direction . What does it mean, for example, the imbalance caused by LL, then we rotate to the right. If it is an imbalance caused by RR, we go to the left, if it is RL, we go to LL and then RR. If it is LR, we first RR and then LL. Well, let's take a look at how it rotates.

   Then the following will be interesting. I hope you can take a closer look.

First, the left rotation of the RR case

  We also have to look at the pictures and talk, I try to make the pictures easy to misunderstand, oh no, easy to understand.

  For me, I'm not very skilled with drawing tools, and I haven't drawn the crooked curves. Let me talk about it...

  Let's look at the picture above, the left rotation is to rotate the left part of the entire tree to the left with 18 as the axis, so that 18 becomes the root node, and 11 becomes the left child node of 18. This rotation is equivalent to reducing the depth of one layer of the word tree on the right side, so that the entire tree becomes a balanced tree. Then there may be the following situation, but it is the same.

  Then this case is the pivot node (18) to be rotated, and the left child node. After the rotation, the left child node 13 of 18 will become the right child node of 11. In fact, it can be simply considered that it was " squeezed " by node 11 after the left rotation .

  In fact, there is another reason why the left child node of 18 will become the right child node of 11 after rotation, that is, the value of the left child node of 18 must be greater than 11 and less than 18 (the picture before rotation) . Why do you think. Then after the rotation, it must also be greater than 11, so it can become the right child node of 11.

First, the right rotation of the LL case

  Then there is nothing to say about the right rotation in the LL case. It is the same as the RR case. Let's go directly to the picture.

  This is absolutely fine, the principle is the same. Just changed the direction.

  There's not much to say about that, right? Let's look at other places. Double spin...

3. Left-handed (RR) and then right-handed (LL) in the case of LR

  Let's go directly to the picture, and then explain it. After explaining this RL situation, we don't need to be verbose. It's good...it's easy, hehe.

  In fact, what makes people a little confused is the name. I specially added a bracket, I hope you don't get confused.

  I don't know if you understand it or not, but I always feel that this picture is not very friendly. Don't care about the small flaws of 8 nodes. Anyway, they are all dotted lines... and the line pointing to node 10 is a dotted line. .....doesn't affect.....hehe.

  Explain that when we need double rotation, the first rotation is the red frame part, that is, if we need double rotation, the pivot points of the two rotations are different, and the pivot point of the first rotation is the parent of the inset node , and the pivot of the second rotation is the grandparent of the inset node . Everyone must pay attention.

  Then there may be a question here, that is, why the 8 node becomes the right child node of the 7 node after the first rotation. This is very important, and it is directly related to whether you understand the rotation of AVL trees.

  Let's look at the first rotation first. If 8 nodes are inserted instead of 10 nodes, then in the first left rotation, node 7 will become the left child node of node 9, and at this time node 8 has nowhere to go. Yes, because 7 occupies my position, this is a neat thing, you can't delete my node just because of a balance, node 8 will definitely not do it, or you will insert me for what...hey? It feels a little wrong... uh.... let's continue.... And the position of node 8 must be smaller than 9 and larger than 7, so we rotate it and make it the right child of node 7 That's it. Hope I made it clear.

  Then at this time, there may still be a situation where the 7 node has a left child node. It is not drawn on it. It doesn't matter. You are the left child node of the 7 node. After the left rotation, you are still in the original position, and no one occupies your position. You don't have to move. Well, that's it... it's over!

4. Right-handed (LL) and left-handed (RR) in the case of RL

  In fact, there is really nothing to say here, I won't explain it at all, everyone can read it for themselves, if you don't understand it, you can read it from the beginning!

  Alas.... Having said a lot, I can finally get to the final code, on the code!

 

// this is how we calculate the height of the current node 
    var heightNode = function (node) {
         // if not then -1 
        if (node ​​=== null ) {
             return -1 ;
        } else {
             // If there is execution logic 
            // Then tell me what I understand here, Math.max compares the size of the left node and the right node, returns the larger value, and then + 1. 
            // Why return the larger value? Because if the left node exists, then the value is 0 (-1 + 1); and if the right node does not exist, then the right node is -1. 
            // But at this time we have height, so we need to select the node with height, that is, the one with the larger value. 
            // Then why +1? Because the height can only be 0 and cannot be -1. The -1 is what we get by subtracting, not by calculating the height. Remember this is calculating height. 
            console.log(Math.max(heightNode(node.left),heightNode(node.right)) + 1 )
             return Math.max(heightNode(node.left),heightNode(node.right)) + 1 ;
        }
    }

    // RR: single rotation to the left 
    var rotationRR = function (node) {
         var tmp = node.right;
        node.right = tmp.left;
        tmp.left = node;
        return tmp;
    }

    // LL: single rotation to the right 
    var rotationLL = function (node) {
         var tmp = node.left;
        node.left = tmp.right;
        tmp.right = node;
        return tmp;
    }

    // LR: get double rotation to the right 
    var rotationLR = function (node) {
        node.left = rotationRR(node.left);
        return rotationLL(node);
    }

    // RL: double rotation to the left 
    var rotationRL = function (node) {
        node.right = rorarionLL(node.right);
        return rorarionRR(node);
    }

    var balanceInsertNode = function (node,element) {
         // If the position of node has no value, just add it directly. 
        if (node ​​=== null ) {
            node = newNode(element);
         // If the value is less than the current node, it means we want to add it to the left of the current node. 
        } else  if (element < node.key) {
            node.left = insertNode(node.left,element);
             // Then it is necessary to judge whether it is null or not. If it is null, then no problem, just add it directly. 
            if (node.left !== null ) {
                 // If not, we need to calculate whether the height of the left side minus the height of the right side of the node is greater than 1. If it is, it means that it is unbalanced, and we need to call the balance method to balance it. 
                if ((heightNode(node.left) - heightNode(node.right)) > 1 ) {
                     // If the value of the currently inserted node is less than the value of node.left, it means LL and we need to rotate right. Otherwise, we need to rotate left first, then right. 
                    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);
                    }
} } } }

   There is one more balanceInsertNode method in the code. This method needs to replace the insertNode method we wrote earlier. This is written for everyone to compare better. The code is not as before, written a lot of comments to explain. In fact, there is a lot to say, which has been said in the previous diagrams and language descriptions. So when you look at this code. If there is something you don't understand, you can definitely understand it by looking at it bit by bit against the logic above. For example, the internal replacement of rotationLL and rotationRR and why they are replaced have been mentioned earlier. So no more verbose in the code.

  This article is a bit long, and it took me a little thought to complete it. Very important, if you want to have a good understanding of trees, these must be known. I will try my best to explain it to you with the ideas I understand. If there is anything unclear, you can leave a message for discussion.

  Oh, by the way, I wanted to tell you about other trees, but it's not necessary after thinking about it. I'll give you a link, and you can do some simple understanding by yourself, such as red-black trees, stacked trees, and B-trees. Etc., etc. many kinds of. It is not enough to finish all the dozens of articles. I hope that these two tree-structured articles can be helpful. Get everyone interested in data structures.

  You can look at this to understand https://zh.wikipedia.org/wiki/AVL%E6%A0%91 , swipe to the bottom of the page, you can see other tree structures.

  Well, finally, the self-balancing binary search tree is basically over here. The next section will cover the last and most complex type of nonlinear data structure—graphs.

 

  Finally, due to my limited level, my ability is still far from that of the Great God. If there are mistakes or unclear points, I hope everyone will give me some advice and corrections. thank you very much!

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325357932&siteId=291194637