Understanding Fenwick Trees / Binary Indexed Trees

Revision en3, by Malomalomalomalo, 2018-01-24 05:52:10

Im writing this both to help others and test myself so I will try to explain everything at a basic level. Any feedback is appreciated.

A Fenwick Tree (a.k.a Binary Indexed Tree, or BIT) is a fairly common data structure. BITs are used to efficiently answer certain types of range queries, on ranges from a root to some distant node. They also allow quick updates on individual data points.

An example of a range query would be this: "What is the sum of the numbers indexed from [1,x]?"

An example of an update would be this: "Increase the number indexed by x by v."

A BIT can perform both of these operations in O(log N) time, and takes O(N) memory.

So how does this work?

BITs take advantage of the fact that ranges can be broken down into other ranges, and combined quickly. Adding the numbers 1 through 4 to the numbers 5 through 8 is the same as adding the numbers 1 through 8. Basically, if we can precalculate the range query for a certain subset of ranges, we can quickly combine them to answer any [1,x] range query.

The binary number system helps us here. Every number N can be represented in log N digits in binary. We can use these digits to construct a tree like so:

The length of an interval that ends at index I is the same as the least significant digit of that number in binary. (We exclude zero as its binary representation doesn't have any ones.) For example, interval ending at 7 (111) has a length of one, 4 (100) has a length of four, six (110) has a length of 2.

This gives the tree some interesting properties which make log N querying and updating possible.

• Every index has exactly one interval ending there. This is obvious from the way we constructed the tree.
• Every range [1,x] is constructable from the intervals given, and every range decomposes into at most log N ranges. (This will be proved below)
• Every index is included in at most log N intervals. (This will also be proved below)

Proof that every range [1,x] is constructable from the intervals given.

A range query can be defined recursively [1,x] = [1,a-1]+[a,x] where [a,x] is the interval ending at x. x's which are powers of two are base cases as they contain the range [1,x] precalced. a is never below 1 as it is defined as the least sig bit in x, and x — (least sig bit) is either positive or a base case.

Proof that the range [1,x] can be broken down into a log N number of intervals.

let len(index) = length of the interval ending at index.

We use the above recursion [1,x] = [1,a-1] + [a,x]. as len(x) = least sig bit in x, and a-1 = x — len(x), the least significant bit in a-1 is greater than len(x) (unless x is a power of two, in which case it is only one interval). (Subtracting len(x) from x removes this bit.) Since len(a-1) > len(x), len(a-1) >= 2 * len(x). This means that we approach 1 at an exponential rate, so it takes log N intervals to construct [1,x].

(Note that visualizing x as a binary integer, and recognizing that at each step in the recursion the least sig bit is turned to zero, and that we end when x reaches 0, means that it takes n steps (where n is the number of bits) at most, and these n bits represent 2^n numbers, so we can reach the logarithmic number of intervals this way too.)

Proof that every index is included in at most log N intervals.

This is essential to proving that updating takes log N operations.

Looking at the pictures this seems true, lets proceed with a proof by contradiction.

Assume intervals ending at a and b with len(a)=len(b) intersect, and without loss of generality let b>a. If these intersect, that means b-len(b)<a. Note that b — len(b) is just removing the least sig bit. As len(a) = len(b) both a and b are indentical from the least sig bit to their start. This also means that as b > a, the binary number above these digits for b is greater than a. Removing the least sig bit from b doesn't change b>a as, in binary, the greater number is determined by the leftmost digit, as every digit carries more weight than all the previous digits combined.

Basically: b = [B]10...00 and a=[A]10...00 with [B]>[A]. b-len(b) = [B]00...00 which is still greater than a, so b cannot intersect a.

As there are a log N number of interval lengths, and no two lengths of the same size intersect, this means that any index is covered by at most log N intervals. Thus, updating an index requires updates to at most log N intervals.

#### History

Revisions

Rev. Lang. By When Δ Comment
en11 Malomalomalomalo 2018-01-25 02:15:16 0 (published)
en10 Malomalomalomalo 2018-01-25 02:14:30 30 Tiny change: 'plements) &mdash; a = [A inv' -> 'plements) \-a = [A inv'
en9 Malomalomalomalo 2018-01-25 02:11:56 173
en8 Malomalomalomalo 2018-01-24 07:14:16 153 Tiny change: '\{ ++x;\n while' -> '\{ ++x;\n\n while'
en7 Malomalomalomalo 2018-01-24 07:02:04 93 Tiny change: 'roblems.\n<code>\n' -> 'roblems.\n\n<code>\n'
en6 Malomalomalomalo 2018-01-24 06:46:51 10
en5 Malomalomalomalo 2018-01-24 06:45:12 1558
en4 Malomalomalomalo 2018-01-24 06:29:36 1677
en3 Malomalomalomalo 2018-01-24 05:52:10 2118
en2 Malomalomalomalo 2018-01-24 05:34:05 1384
en1 Malomalomalomalo 2018-01-23 06:11:10 1300 Initial revision (saved to drafts)