Skip to content

Commit e4a046e

Browse files
committed
feat: add Fenwick Tree implementation with tests
1 parent 5c39e87 commit e4a046e

2 files changed

Lines changed: 149 additions & 0 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* Time Complexity:
3+
* Update: O(log n)
4+
* Query: O(log n)
5+
* Range Query: O(log n)
6+
*
7+
* Space Complexity: O(n)
8+
*
9+
*/
10+
11+
export default class FenwickTree {
12+
constructor(size) {
13+
this.size = size
14+
this.tree = new Array(size + 1).fill(0)
15+
}
16+
17+
static fromArray(arr) {
18+
const ft = new FenwickTree(arr.length)
19+
for (let i = 0; i < arr.length; i++) {
20+
ft.update(i, arr[i])
21+
}
22+
return ft
23+
}
24+
25+
update(index, delta) {
26+
let i = index + 1
27+
while (i <= this.size) {
28+
this.tree[i] += delta
29+
i += i & -i // Adding the lowest set bit
30+
}
31+
}
32+
33+
query(index) {
34+
let i = index + 1
35+
let sum = 0
36+
while (i > 0) {
37+
sum += this.tree[i]
38+
i -= i & -i // Removing the lowest set bit
39+
}
40+
return sum
41+
}
42+
43+
rangeQuery(left, right) {
44+
if (left === 0) return this.query(right)
45+
return this.query(right) - this.query(left - 1)
46+
}
47+
48+
get(index) {
49+
return this.rangeQuery(index, index)
50+
}
51+
52+
set(index, value) {
53+
const current = this.get(index)
54+
this.update(index, value - current)
55+
}
56+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import FenwickTree from '../FenwickTree'
2+
3+
describe('Fenwick Tree', () => {
4+
describe('Basic Operations', () => {
5+
test('should handle point updates and prefix queries', () => {
6+
const ft = new FenwickTree(5)
7+
ft.update(0, 1)
8+
ft.update(1, 2)
9+
ft.update(2, 3)
10+
ft.update(3, 4)
11+
ft.update(4, 5)
12+
13+
expect(ft.query(0)).toBe(1)
14+
expect(ft.query(1)).toBe(3)
15+
expect(ft.query(2)).toBe(6)
16+
expect(ft.query(3)).toBe(10)
17+
expect(ft.query(4)).toBe(15)
18+
})
19+
20+
test('should handle range queries', () => {
21+
const ft = new FenwickTree(5)
22+
ft.update(0, 10)
23+
ft.update(1, 20)
24+
ft.update(2, 30)
25+
ft.update(3, 40)
26+
ft.update(4, 50)
27+
28+
expect(ft.rangeQuery(1, 3)).toBe(90)
29+
expect(ft.rangeQuery(0, 2)).toBe(60)
30+
expect(ft.rangeQuery(2, 4)).toBe(120)
31+
})
32+
})
33+
34+
describe('Edge Cases', () => {
35+
test('should handle empty tree', () => {
36+
const ft = new FenwickTree(3)
37+
expect(ft.query(0)).toBe(0)
38+
expect(ft.query(1)).toBe(0)
39+
expect(ft.query(2)).toBe(0)
40+
})
41+
42+
test('should handle single element', () => {
43+
const ft = new FenwickTree(1)
44+
ft.update(0, 42)
45+
expect(ft.query(0)).toBe(42)
46+
expect(ft.rangeQuery(0, 0)).toBe(42)
47+
})
48+
49+
test('should handle negative values', () => {
50+
const ft = new FenwickTree(3)
51+
ft.update(0, 5)
52+
ft.update(1, -2)
53+
ft.update(2, 3)
54+
55+
expect(ft.rangeQuery(0, 2)).toBe(6)
56+
expect(ft.rangeQuery(0, 1)).toBe(3)
57+
})
58+
})
59+
60+
describe('Get and Set', () => {
61+
test('should get value at specific index', () => {
62+
const ft = new FenwickTree(3)
63+
ft.update(0, 5)
64+
ft.update(1, 10)
65+
ft.update(2, 15)
66+
67+
expect(ft.get(0)).toBe(5)
68+
expect(ft.get(1)).toBe(10)
69+
expect(ft.get(2)).toBe(15)
70+
})
71+
72+
test('should set value at specific index', () => {
73+
const ft = new FenwickTree(3)
74+
ft.update(0, 5)
75+
ft.update(1, 10)
76+
ft.update(2, 15)
77+
78+
ft.set(1, 20)
79+
expect(ft.get(1)).toBe(20)
80+
expect(ft.rangeQuery(0, 2)).toBe(40)
81+
})
82+
})
83+
84+
describe('Static Methods', () => {
85+
test('should build from array', () => {
86+
const arr = [1, 2, 3, 4, 5]
87+
const ft = FenwickTree.fromArray(arr)
88+
89+
expect(ft.query(4)).toBe(15)
90+
expect(ft.rangeQuery(1, 3)).toBe(9)
91+
})
92+
})
93+
})

0 commit comments

Comments
 (0)