Skip to content

Commit 1454129

Browse files
committed
implemented binary indexed tree.
1 parent 2fdd8b8 commit 1454129

File tree

6 files changed

+274
-0
lines changed

6 files changed

+274
-0
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
let array = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
2+
3+
let tree = BinaryIndexedTree<Double>(array: array)
4+
5+
// calculates the sum within [0-5] inclusive. Should be 21
6+
tree.query(from: 0, to: 5)
7+
8+
// updates value at index 4 from 5.0 to 0.5
9+
tree.update(at: 4, with: 0.5)
10+
11+
// calculates the sum within [0-5] inclusive. Should be 16.5
12+
tree.query(from: 0, to: 5)
13+
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
Data structure that can efficiently update elements and calculate prefix sums in a table of numbers
3+
*/
4+
public class BinaryIndexedTree<T: Numeric> {
5+
6+
private var values: [T]
7+
private var tree: [T]
8+
9+
public init(array: [T]) {
10+
self.values = Array(repeating: 0, count: array.count)
11+
12+
// first element (ie index 0) is root
13+
self.tree = Array(repeating: 0, count: array.count + 1)
14+
15+
for (index, value) in array.enumerated() {
16+
update(at: index, with: value)
17+
}
18+
}
19+
20+
// Updates value at given index with a new value
21+
// Complexity: O(ln(n))
22+
public func update(at index: Int, with value: T) {
23+
updateInternally(at: index, with: value - values[index])
24+
}
25+
26+
// Calculates sum of the given interval.
27+
// Inteval given by [from - to] inclusive
28+
// Complexity: O(ln(n))
29+
public func query(from lowerIndex: Int, to upperIndex: Int) -> T {
30+
return queryInternally(to: upperIndex) - queryInternally(to: lowerIndex - 1)
31+
}
32+
33+
private func updateInternally(at index: Int, with diff: T) {
34+
self.values[index] += diff
35+
36+
var treeIndex = index + 1
37+
while isValid(index: treeIndex) {
38+
tree[treeIndex] += diff
39+
treeIndex = sibling(for: treeIndex)
40+
}
41+
}
42+
43+
private func queryInternally(to upperIndex: Int) -> T {
44+
var sum: T = 0
45+
46+
var treeIndex = upperIndex + 1
47+
48+
while isValid(index: treeIndex) {
49+
sum += tree[treeIndex]
50+
treeIndex = parent(for: treeIndex)
51+
}
52+
53+
return sum
54+
}
55+
56+
private func isValid(index: Int) -> Bool {
57+
return index > 0 && index < tree.count
58+
}
59+
60+
private func parent(for index: Int) -> Int {
61+
return index - index & (~index + 1);
62+
}
63+
64+
private func sibling(for index: Int) -> Int {
65+
return index + index & (~index + 1);
66+
}
67+
68+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<playground version='5.0' target-platform='macos' buildActiveScheme='true'>
3+
<timeline fileName='timeline.xctimeline'/>
4+
</playground>

Binary Indexed Tree (Fenwick Tree)/Binary Indexed Tree.playground/playground.xcworkspace/contents.xcworkspacedata

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
Data structure that can efficiently update elements and calculate prefix sums in a table of numbers
3+
*/
4+
public class BinaryIndexedTree<T: Numeric> {
5+
6+
private var values: [T]
7+
private var tree: [T]
8+
9+
public init(array: [T]) {
10+
self.values = Array(repeating: 0, count: array.count)
11+
12+
// first element (ie index 0) is root
13+
self.tree = Array(repeating: 0, count: array.count + 1)
14+
15+
for (index, value) in array.enumerated() {
16+
update(at: index, with: value)
17+
}
18+
}
19+
20+
// Updates value at given index with a new value
21+
// Complexity: O(ln(n))
22+
public func update(at index: Int, with value: T) {
23+
updateInternally(at: index, with: value - values[index])
24+
}
25+
26+
// Calculates sum of the given interval.
27+
// Inteval given by [from - to] inclusive
28+
// Complexity: O(ln(n))
29+
public func query(from lowerIndex: Int, to upperIndex: Int) -> T {
30+
return queryInternally(to: upperIndex) - queryInternally(to: lowerIndex - 1)
31+
}
32+
33+
private func updateInternally(at index: Int, with diff: T) {
34+
self.values[index] += diff
35+
36+
var treeIndex = index + 1
37+
while isValid(index: treeIndex) {
38+
tree[treeIndex] += diff
39+
treeIndex = sibling(for: treeIndex)
40+
}
41+
}
42+
43+
private func queryInternally(to upperIndex: Int) -> T {
44+
var sum: T = 0
45+
46+
var treeIndex = upperIndex + 1
47+
48+
while isValid(index: treeIndex) {
49+
sum += tree[treeIndex]
50+
treeIndex = parent(for: treeIndex)
51+
}
52+
53+
return sum
54+
}
55+
56+
private func isValid(index: Int) -> Bool {
57+
return index > 0 && index < tree.count
58+
}
59+
60+
private func parent(for index: Int) -> Int {
61+
return index - index & (~index + 1);
62+
}
63+
64+
private func sibling(for index: Int) -> Int {
65+
return index + index & (~index + 1);
66+
}
67+
68+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Binary Indexed (Fenwick) tree
2+
3+
Fenwick Tree is a data structure that can efficiently update elements and calculate prefix sums in a table of numbers.
4+
5+
## Motivation
6+
7+
Let's begin with a small example. Imagine we have an array of numbers:
8+
9+
```
10+
[1, 2, 3, 4, 5, 6]
11+
```
12+
13+
We would like to support:
14+
15+
1. query the sum on some interval
16+
17+
2. update the value of a specified element
18+
19+
There are a lot of ways to implement these operations:
20+
21+
- The most straightforward way is to keep array "as is". Complexity of sum operation would be **O(n)** as we need to calculate sum each time we query it, but the complexity of update will be **O(1)**.
22+
23+
- Next approach comes from dynamic programming. We can precalculate prefix sums inside of some buffer. For the given example precalculated prefix sum is `[1, 3, 6, 10, 15, 21]`. Then we can observe that cumulative sum on interval `(lower-upper)` is `precalculateSum[upper] - precalculateSum[lower] + values[lower]`. Complexity of sum query is **O(1)**, but supporting update operation requires to update all precalculated sums ahead of given index, so, it is **O(n)** for update operation.
24+
25+
- Fenwick tree allows to support these operations in efficient way. Both complexities are **O(ln(n))**.
26+
27+
## Basic
28+
29+
Fenwick Tree supports two basic operations:
30+
31+
1. **update(index, newValue)**: Updates value inside of an array at the given index
32+
33+
2. **query(lowerBound, upperBound)**: Calculates desired cumulative sum on a give range.
34+
35+
36+
## Basic idea
37+
38+
Binary indexed tree can be represented as an array of size **n + 1**, where **n** is a length of initial array. The data structure is called tree as there is nice representation the data structure as tree.
39+
40+
Basic idea of binary indexed tree is based on a fact that any number can be represented as the sum of powers of 2. For example:
41+
42+
```
43+
7 = 2^2 + 2^1 + 2^0
44+
32 = 2^5
45+
13 = 2^3 + 2^2 + 2^0
46+
```
47+
48+
Each node of the tree stores the sum on the interval from **node_index - 2^b** exclusive to **node_index** inclusive. The **b** stands for the position of last significant bit of the **node_index**. The __0th__ node is a dummy node and keep 0.
49+
50+
```
51+
array <- [ 1, 2, 3, 4, 5, 6] (array indexed starting from 1)
52+
53+
stored range <- [ 0 - 0 (dummy, no associated array values), 0 - 1, 0 - 2, 2 - 3, 0 - 4, 4 - 5, 4 - 6]
54+
55+
tree <- [ 0 (dummy), 1, 3, 3, 10, 5, 11]
56+
57+
```
58+
59+
## Sum
60+
61+
To calculate the sum on the interval from **1** up to **val**:
62+
63+
1. While the given index bigger than `0`:
64+
65+
1.1 Find the end of the current interval by extracting last significant byte. To extract last byte you can use the following rule `val - val & (~val + 1)`. This number is the starting point of interval associated with node, when the original one is the end. **3** node keeps the representation of the interval.
66+
67+
1.2 Accumulate the current node value, which is stored inside of a tree. The desired value is tree[index]
68+
69+
1.3 Update the number to the start of it's interval.
70+
71+
1.4 Repeat
72+
73+
For example to calculate the sum from **1** to **3** for the array [**1, 2, 3**, 4, 5, 6] we should do:
74+
75+
0. Tree for thus array looks like [ 0 (dummy), 1, 3, 3, 10, 5, 11].
76+
77+
1. Index is **3**
78+
79+
1.1 `3 = 3 - 3 & (~3 + 1) = 3 - 3 & (0 + 1) = 3 - 3 & 1 = 3 - 1 = 2`, so the interval of the node tree[3] (tree is 0 indexed) is [2-3]
80+
81+
1.2 Sum is 3
82+
83+
1.3 Update the index to **2**
84+
85+
2. Index is **2**
86+
87+
2.1 `2 = 2 - 2 & (~2 + 1) = 2 - 2 & (1 + 1) = 2 - 2 & 2 = 0`. The interval is [0-2], node is tree[2]
88+
89+
2.2 Sum is 3 (from step 1) + 3 (from current step, tree[2])
90+
91+
2.3 Update the index to **0**
92+
93+
3. Index is **0**. Returned sum is **6**.
94+
95+
## Update
96+
97+
Update works like the sum, except it looking for a intervals ahead.
98+
99+
1. While the given index smaller than `n + 1`:
100+
101+
1.1 Find the start of the new interval by extracting last significant byte. To do it you can use the following rule `val + val & (~val + 1)`.
102+
103+
1.2 Accumulate the current node value
104+
105+
1.3 Update the number to the start of following interval.
106+
107+
108+
## See also
109+
110+
See the playground for more examples of how to use this data structure.
111+
112+
[Fenwick Tree at Wikipedia](https://en.wikipedia.org/wiki/Fenwick_tree)
113+
114+
*Written for Swift Algorithm Club by [Alexander Dadukin](https://github.com/st235)

0 commit comments

Comments
 (0)