Tree-like array (Binary Indexed tree)
The previous articles we share are about the interval summation problem of several solutions, but also introduced the line tree such data structure, we can appreciate the reasonable solution brought convenience, for most of the interval problem, line tree has its absolute advantage, today this article, Let's take a look at another data structure that is deformed by the segment tree-a tree array, which is often used to solve the problem of interval summation and single-point updating, and is more efficient than a segment tree (both the tree array interval sum and the time complexity of a single point update are O (log n)), relative to Segment trees can be applied more broadly. But it has to be admitted that a tree array is indeed an elegant and efficient structure. Next, we will come together to uncover its mystery.
1th, the structure of the tree array is similar to the segment tree, but fewer nodes than the segment tree, 2nd, the corresponding range of elements stored in each node of the tree array, as well as the line tree, the tree array is also the array storage structure, 3rd, the tree array and the line segment tree subscript definition rules are different. As shown, this is the storage diagram for the segment tree:
We can find from the previous article (article Link: line segment tree second bomb (interval update)), the line segment tree subscript coding rules are from top to bottom, from left to right, in order to encode, in the tree array, no longer use such a coding method, how the code will be explained later, now let's look at the characteristics of the line segment tree.
We assume that each node in this segment tree stores the and of all elements within the corresponding interval range. At this point, we can find that for each right child node value, we always by its father node value minus the left sibling node value to calculate, which means that even if there is no right node, also does not affect our solution to the interval of the corresponding interval and, then we do not save space, but this raises another problem, After the removal of all the right node, the previous subscript encoding method must not be used, this time we need a new subscript coding, both to save this part of the space, and not to complicate the original problem, it is best to further simplify the problem, this is the background of the tree array. As for the new set of coding way to go through the process of exploration, not the focus we have to explain today, I do not make too much explanation, and now I directly throw the tree array to determine the final version of the subscript encoding, the reason for using this way is of course: it is simple ah, easy to use Ah, elegant ah, why? After reading this article, you will probably be able to appreciate its charm.
As shown in the logical structure of the tree array, where each node is stored is still the element of the corresponding interval and:
This is the logical structure of the tree array, where the number in the box indicates the subscript range of the corresponding interval, the red font indicates the subscript of the node, and the blue lines in the figure Connect each node and its corresponding subscript (the line is so thick, probably no one can see it clearly). At first glance, think of the node subscript encoding method seems a bit vexatious, the same level of two nodes is not connected, what is the logic? Whenever we feel cornered, that is, we need to re-examine all the clues in the hand, only to let go of any small clue, to find a way to solve the law. Let's make a list of all the clues we can see.
What we have described above is actually the logical structure of a tree array, and its physical storage is a one-dimensional array. Our features are tabulated as follows:
Observing the above table, we can draw the following conclusions:
When the node is labeled I, the corresponding last element in the node is labeled I
Second, the node subscript corresponding to the binary number at the end of the K 0, the number of elements corresponding to the node is 2 ^ k
Third, the corresponding element subscript in the node is continuous
Iv. the number of nodes in the tree array is equal to the number of original data elements
These are the main basic features of the tree array, and when you know these features, we can find that to change the value of an element in the original data array, you need to change the value of O (log n) nodes in the tree array, so the time complexity of the single point update is O (log n). The specific implementation of the single-point update, at the end of the article will show you, and now continue to discuss the next question.
At this time we will find that all the features listed just now seem to be useless, just as the fragmented knowledge, skills, and contacts we have in our hands seem to be scattered, and we don't know when to use them, or even spend our entire lives, but once we have a chance to use them, We can truly realize how important those are, and how closely the connections are. Learning algorithms is not so much a technique as learning an art, because there are many ways in which you can find a shadow from your life. So let's not lose heart for the time being, continue to study, perhaps more deeply, these trivial features will become useful.
A tree-like array is actually "the first I element and" the problem,
Consider the example:
First 1 elements and for
SUM[1] = sum[0001] = tree[1]= tree[0001]
First 2 elements and for
SUM[2] = sum[0010] = tree[2]=tree[0010]
First 3 elements and for
SUM[3] = sum[0011] = tree[2]+tree[3]= tree[0011] + tree[0010]
First 4 elements and for
SUM[4] =sum[0100] = tree[4]= tree[0100]
First 5 elements and for
SUM[5] = sum[0101]= tree[4]+tree[5]=tree[0101]+tree[0100]
First 6 elements and for
SUM[6] = sum[0110] = tree[4]+tree[6]= tree[0110] + tree[0100]
First 7 elements and for
SUM[7]=SUM[0111]=TREE[7]+TREE[6]+TREE[4]=TREE[0111]+TREE[0110]+TREE[0100]
First 8 elements and for
SUM[8] = sum[1000] = tree[8]= tree[1000]
The red part is the subscript binary representation form, I think so far, we may really is cornered, just fair bet, even subscript also not let go. Careful observation of these subscript, there seems to be a certain pattern to follow, now we pick an expression of the longest, can explain the problem to study
SUM[7]=SUM[0111]=TREE[7]+TREE[6]+TREE[4]=TREE[0111]+TREE[0110]+TREE[0100]
From the above expression can be found that the first 7 elements and sum[0111] addend including tree[0111], on this basis, each time the subscript from the right-to-left number first 1 erase as the next addend subscript, until the number becomes 0 end, 0111 erase the last 1 get 0110,0110 erase Go to the last one 1 get 0100,0100 erase last one 1 get 0000 end operation. This seems to be barely counted as a rule, and it has been proved that all of the above expressions conform to this rule. Here I can tell you, after countless experts to verify that the law does exist, so we can boldly use.
However, when we use words to describe the time, we can say erase the last 1, in the actual implementation, we need to use the standard language to express, to achieve erase the last 1 effect, you need to subtract a number x, 0111 erase the last one 1 to get 0110, x = 0001, 0110 erase The last one 1 gets 0100, x = 0010, and 0100 erases the last 1 o'clock, x = 0100. It also means that the original value needs to erase which one, then which of X is 1, the rest of you are 0. How to find the original value which one is the last 1? We can see that the original value is at the bottom of K 0 o'clock, x = 2 ^ k, and now we have a link to the features listed in front of us. At present our task is to seek the value of K, of course, for people, a glance at the end of a number of a few 0 is easy, but for the computer, it seems not so easily, at this time if the original value of the binary number as a whole, it seems unreasonable, we need to separate its members, which is used in the bit operation. For the current problem, there is a solution skill can be shared to everyone, we know that in the computer two numbers of operations with the complement of the implementation, for positive, the complement and the original code form is the same, but for negative, the complement is the original code to take the reverse position after the last plus 1, This results in the complement of a negative absolute value and the complement of this negative number in the form of: the first non-0-bit right-to-left, on the left, the absolute value of the complement of negative numbers and the complement of the negative is not the same, on the right side, the absolute value of the complement and negative number of the complement of everyone is 0 The number of calculations is exactly what we need X, we know the absolute and negative numbers of the negative itself are opposite to each other, but also shows that a positive complement and the opposite number of the complement to do and operation of the number is X.
The validation examples are as follows:
1 & (-1) complement operation: 0001 & 1111 = 0001
2 & (-2) complement operation: 0010 & 1110 = 0010
3 & (-3) complement operation: 0011 & 1101 = 0001
4 & (-4) complement operation: 0100 & 1100 = 0100
5 & (-5) complement operation: 0101 & 1011 = 0001
6 & (-6) complement operation: 0110 & 1010 = 0010
7 & (-7) complement operation: 0111 & 1001 = 0001
8 & (-8) Complement operation: + & 1000 = 1000
Thus, we get the X =i & (-i)
At this point, the law of the first n is found, and our task is to describe the rule in canonical statements and try to implement it in code.
The summation concrete implementation is described as follows:
Assuming the sum of the current term I
The first step: determine whether I > 0 is established. If the next step is set up, otherwise exit the loop
Step two: sum = sum + tree[i], x= I & (-i)
Step three: i = I-x, back to the first step
Once I know the solution of the first I and the solution, it is much easier to solve the interval and relatively, for example, the solution interval is I ~ j, obviously, we can know
sum[I~j] = sum [j]-sum [i].
After solving the problems of the first I and then, let's go back and analyze the issue of the single point update, or just the diagram:
Assuming that the element we are modifying now is labeled 3 in the original data, then in the tree array we need to modify the node subscript to 3, 4, 8, respectively.
or in accordance with the analysis method, in the original data to modify the element subscript 3 = 0011
In a tree-like array, the nodes that need to be modified are labeled 3 = 0011, 4 = 0100, 8 = 1000,
observed, 0011 +0001 = 0100, 0100 +0100 = 1000, did you find the pattern? The X-value is still just the method, and now each time you give the current subscript value plus x to get the next Addend value, until the subscript value is greater than the total number of elements stop. Specific examples but more than repeat, we can personally verify.
The specific description of the single-point update is as follows: (assuming the updated rule is to add Y to the element labeled I)
Assuming that the element to be updated is labeled I, the total number of elements is n
The first step: determine whether I <= N is established, set up the next step, do not set up the end of the cycle
Step two: tree[i] = tree[i] + y, x = i & (-i), proceed to next step
Step three: i = i + x, jump back to the first step
The above is the theoretical part of today's content, the following for everyone to serve the core code implementation part, I hope today's sharing can let everyone have harvest. The code is as follows:
In addition to the collection, this should be the most streamlined and elegant and efficient code ever seen.
A friend who has not paid attention to the public number can long press the QR code in the identification chart to follow me.
The usual, open the Web page http://paste.ubuntu.com/25548013/see the Web version of the code.
Tree-like Array (Binary Indexed tree,bit)