Skip to content

Commit 183dade

Browse files
committed
Update Fenwick Tree readme and do code style fixes.
1 parent 1a4fe11 commit 183dade

File tree

4 files changed

+129
-37
lines changed

4 files changed

+129
-37
lines changed

Diff for: README.md

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ the data.
3333
* [AVL Tree](https://github.com/trekhleb/javascript-algorithms/tree/master/src/data-structures/tree/avl-tree)
3434
* [Red-Black Tree](https://github.com/trekhleb/javascript-algorithms/tree/master/src/data-structures/tree/red-black-tree)
3535
* [Segment Tree](https://github.com/trekhleb/javascript-algorithms/tree/master/src/data-structures/tree/segment-tree) - with min/max/sum range queries examples
36+
* [Fenwick Tree](https://github.com/trekhleb/javascript-algorithms/tree/master/src/data-structures/tree/fenwick-tree) (Binary Indexed Tree)
3637
* [Graph](https://github.com/trekhleb/javascript-algorithms/tree/master/src/data-structures/graph) (both directed and undirected)
3738
* [Disjoint Set](https://github.com/trekhleb/javascript-algorithms/tree/master/src/data-structures/disjoint-set)
3839

Diff for: src/data-structures/tree/fenwick-tree/FenwickTree.js

+52-29
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,72 @@
11
export default class FenwickTree {
22
/**
3-
* Constructor creates empty fenwick tree of size 'size',
4-
* however, array size is size+1, because index is 1-based
5-
* @param {number} [size]
3+
* Constructor creates empty fenwick tree of size 'arraySize',
4+
* however, array size is size+1, because index is 1-based.
5+
*
6+
* @param {number} arraySize
67
*/
7-
constructor(size) {
8-
this.n = size;
9-
this.arr = [];
10-
for (let i = 0; i <= size; i += 1) this.arr.push(0);
8+
constructor(arraySize) {
9+
this.arraySize = arraySize;
10+
11+
// Fill tree array with zeros.
12+
this.treeArray = Array(this.arraySize + 1).fill(0);
1113
}
1214

1315
/**
14-
* Adds v to index x
15-
* @param {number} [x]
16-
* @param {number} [v]
16+
* Adds value to position.
17+
*
18+
* @param {number} position
19+
* @param {number} value
20+
* @return {FenwickTree}
1721
*/
18-
update(x, v) {
19-
if (x < 1 || x > this.n) return;
20-
for (let i = x; i <= this.n; i += (i & -i)) {
21-
this.arr[i] += v;
22+
update(position, value) {
23+
if (position < 1 || position > this.arraySize) {
24+
throw new Error('Position is out of allowed range');
25+
}
26+
27+
for (let i = position; i <= this.arraySize; i += (i & -i)) {
28+
this.treeArray[i] += value;
2229
}
30+
31+
return this;
2332
}
2433

2534
/**
26-
* query sum from index 1 to x
27-
* @param {number} [x]
28-
* @return {number} sum
35+
* Query sum from index 1 to position.
36+
*
37+
* @param {number} position
38+
* @return {number}
2939
*/
30-
query(x) {
31-
if (x > this.n) return this.query(this.n);
32-
let ret = 0;
33-
for (let i = x; i > 0; i -= (i & -i)) {
34-
ret += this.arr[i];
40+
query(position) {
41+
if (position < 1 || position > this.arraySize) {
42+
throw new Error('Position is out of allowed range');
3543
}
36-
return ret;
44+
45+
let sum = 0;
46+
47+
for (let i = position; i > 0; i -= (i & -i)) {
48+
sum += this.treeArray[i];
49+
}
50+
51+
return sum;
3752
}
3853

3954
/**
40-
* query sum from index l to r
41-
* @param {number} [l]
42-
* @param {number} [r]
55+
* Query sum from index leftIndex to rightIndex.
56+
*
57+
* @param {number} leftIndex
58+
* @param {number} rightIndex
4359
* @return {number}
4460
*/
45-
queryRange(l, r) {
46-
if (l > r) return 0;
47-
return this.query(r) - this.query(l - 1);
61+
queryRange(leftIndex, rightIndex) {
62+
if (leftIndex > rightIndex) {
63+
throw new Error('Left index can not be greater then right one');
64+
}
65+
66+
if (leftIndex === 1) {
67+
return this.query(rightIndex);
68+
}
69+
70+
return this.query(rightIndex) - this.query(leftIndex - 1);
4871
}
4972
}

Diff for: src/data-structures/tree/fenwick-tree/README.md

+14-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
1-
# Binary Indexed Tree / Fenwick Tree
1+
# Fenwick Tree / Binary Indexed Tree
22

3-
A simple data structure that supports fast range queries in an array. However, it is usually only valid for reversible operations, like addition and subtraction
3+
A simple data structure that supports fast range queries
4+
in an array. However, it is usually only valid for reversible
5+
operations, like addition and subtraction
46

5-
This implementation uses the basic range sum query and point update. All the indexes are 1-based
7+
Binary Indexed Tree is represented as an array. Each node of Binary Indexed Tree
8+
stores sum of some elements of given array. Size of Binary Indexed Tree is equal
9+
to `n` where `n` is size of input array. In current implementation we have used
10+
size as `n+1` for ease of implementation. All the indexes are 1-based.
11+
12+
![Binary Indexed Tree](https://www.geeksforgeeks.org/wp-content/uploads/BITSum.png)
13+
14+
## References
615

716
- [Wikipedia](https://en.wikipedia.org/wiki/Fenwick_tree)
8-
- [Geeksforgeeks](https://www.geeksforgeeks.org/binary-indexed-tree-or-fenwick-tree-2/)
17+
- [GeeksForGeeks](https://www.geeksforgeeks.org/binary-indexed-tree-or-fenwick-tree-2/)
18+
- [YouTube](https://www.youtube.com/watch?v=CWDQJGaN1gY&index=18&t=0s&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)

Diff for: src/data-structures/tree/fenwick-tree/__test__/FenwickTree.test.js

+62-4
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,44 @@ import FenwickTree from '../FenwickTree';
33
describe('FenwickTree', () => {
44
it('should create empty fenwick tree of correct size', () => {
55
const tree1 = new FenwickTree(5);
6-
expect(tree1.arr.length).toBe(5 + 1);
6+
expect(tree1.treeArray.length).toBe(5 + 1);
77

88
for (let i = 0; i < 5; i += 1) {
9-
expect(tree1.arr[i]).toBe(0);
9+
expect(tree1.treeArray[i]).toBe(0);
1010
}
1111

1212
const tree2 = new FenwickTree(50);
13-
expect(tree2.arr.length).toBe(50 + 1);
13+
expect(tree2.treeArray.length).toBe(50 + 1);
14+
});
15+
16+
it('should create correct fenwick tree', () => {
17+
const inputArray = [3, 2, -1, 6, 5, 4, -3, 3, 7, 2, 3];
18+
19+
const tree = new FenwickTree(inputArray.length);
20+
expect(tree.treeArray.length).toBe(inputArray.length + 1);
21+
22+
inputArray.forEach((value, index) => {
23+
tree.update(index + 1, value);
24+
});
25+
26+
expect(tree.treeArray).toEqual([0, 3, 5, -1, 10, 5, 9, -3, 19, 7, 9, 3]);
27+
28+
expect(tree.query(1)).toBe(3);
29+
expect(tree.query(2)).toBe(5);
30+
expect(tree.query(3)).toBe(4);
31+
expect(tree.query(4)).toBe(10);
32+
expect(tree.query(5)).toBe(15);
33+
expect(tree.query(6)).toBe(19);
34+
expect(tree.query(7)).toBe(16);
35+
expect(tree.query(8)).toBe(19);
36+
expect(tree.query(9)).toBe(26);
37+
expect(tree.query(10)).toBe(28);
38+
expect(tree.query(11)).toBe(31);
39+
40+
expect(tree.queryRange(1, 1)).toBe(3);
41+
expect(tree.queryRange(1, 2)).toBe(5);
42+
expect(tree.queryRange(2, 4)).toBe(7);
43+
expect(tree.queryRange(6, 9)).toBe(11);
1444
});
1545

1646
it('should correctly execute queries', () => {
@@ -31,7 +61,35 @@ describe('FenwickTree', () => {
3161
expect(tree.queryRange(1, 1)).toBe(7);
3262
expect(tree.query(5)).toBe(19);
3363
expect(tree.queryRange(1, 5)).toBe(19);
64+
});
65+
66+
it('should throw exceptions', () => {
67+
const tree = new FenwickTree(5);
68+
69+
const updateAtInvalidLowIndex = () => {
70+
tree.update(0, 1);
71+
};
72+
73+
const updateAtInvalidHighIndex = () => {
74+
tree.update(10, 1);
75+
};
76+
77+
const queryInvalidLowIndex = () => {
78+
tree.query(0);
79+
};
80+
81+
const queryInvalidHighIndex = () => {
82+
tree.query(10);
83+
};
84+
85+
const rangeQueryInvalidIndex = () => {
86+
tree.queryRange(3, 2);
87+
};
3488

35-
expect(tree.queryRange(5, 1)).toBe(0); // invalid test
89+
expect(updateAtInvalidLowIndex).toThrowError();
90+
expect(updateAtInvalidHighIndex).toThrowError();
91+
expect(queryInvalidLowIndex).toThrowError();
92+
expect(queryInvalidHighIndex).toThrowError();
93+
expect(rangeQueryInvalidIndex).toThrowError();
3694
});
3795
});

0 commit comments

Comments
 (0)