Skip to content

Commit e3d6222

Browse files
committed
Add Fenwick tree implementation and tests
1 parent f5772e3 commit e3d6222

2 files changed

Lines changed: 134 additions & 8 deletions

File tree

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,33 @@
11
package com.thealgorithms.datastructures.trees;
22

3+
/** Fenwick Tree for point updates and prefix-sum queries in O(log n). */
34
public class FenwickTree {
45

5-
private int n;
6-
private int[] fenTree;
6+
private final int n;
7+
private final int[] fenTree;
78

8-
/* Constructor which takes the size of the array as a parameter */
9+
/** Creates a Fenwick tree with n elements, all initialized to zero. */
910
public FenwickTree(int n) {
11+
if (n < 0) {
12+
throw new IllegalArgumentException("Size must be non-negative");
13+
}
1014
this.n = n;
1115
this.fenTree = new int[n + 1];
1216
}
1317

14-
/* A function which will add the element val at index i*/
18+
/** Adds val to the element at index i. */
1519
public void update(int i, int val) {
16-
// As index starts from 0, increment the index by 1
17-
i += 1;
20+
checkIndex(i);
21+
i += 1; // Convert to the internal 1-based index
1822
while (i <= n) {
1923
fenTree[i] += val;
2024
i += i & (-i);
2125
}
2226
}
2327

24-
/* A function which will return the cumulative sum from index 1 to index i*/
28+
/** Returns the sum of elements from index 0 to i. */
2529
public int query(int i) {
26-
// As index starts from 0, increment the index by 1
30+
checkIndex(i);
2731
i += 1;
2832
int cumSum = 0;
2933
while (i > 0) {
@@ -32,4 +36,11 @@ public int query(int i) {
3236
}
3337
return cumSum;
3438
}
39+
40+
// Check that the index is within the valid range.
41+
private void checkIndex(int i) {
42+
if (i < 0 || i >= n) {
43+
throw new IndexOutOfBoundsException("Index " + i + " out of bounds for size " + n);
44+
}
45+
}
3546
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package com.thealgorithms.datastructures.trees;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
6+
import java.util.Random;
7+
import org.junit.jupiter.api.Test;
8+
9+
class FenwickTreeTest {
10+
11+
@Test
12+
void queryOnFreshTreeReturnsZero() {
13+
FenwickTree tree = new FenwickTree(5);
14+
for (int i = 0; i < 5; i++) {
15+
assertEquals(0, tree.query(i));
16+
}
17+
}
18+
19+
@Test
20+
void singleElementTree() {
21+
FenwickTree tree = new FenwickTree(1);
22+
tree.update(0, 7);
23+
assertEquals(7, tree.query(0));
24+
}
25+
26+
@Test
27+
void prefixSumsAfterUpdates() {
28+
FenwickTree tree = new FenwickTree(5);
29+
int[] values = {3, 2, -1, 6, 5};
30+
for (int i = 0; i < values.length; i++) {
31+
tree.update(i, values[i]);
32+
}
33+
assertEquals(3, tree.query(0));
34+
assertEquals(5, tree.query(1));
35+
assertEquals(4, tree.query(2));
36+
assertEquals(10, tree.query(3));
37+
assertEquals(15, tree.query(4));
38+
}
39+
40+
@Test
41+
void repeatedUpdatesOnSameIndexAccumulate() {
42+
FenwickTree tree = new FenwickTree(4);
43+
tree.update(2, 5);
44+
tree.update(2, 3);
45+
tree.update(2, -2);
46+
assertEquals(0, tree.query(1));
47+
assertEquals(6, tree.query(2));
48+
assertEquals(6, tree.query(3));
49+
}
50+
51+
@Test
52+
void negativeValues() {
53+
FenwickTree tree = new FenwickTree(3);
54+
tree.update(0, -4);
55+
tree.update(1, -6);
56+
tree.update(2, 10);
57+
assertEquals(-4, tree.query(0));
58+
assertEquals(-10, tree.query(1));
59+
assertEquals(0, tree.query(2));
60+
}
61+
62+
@Test
63+
void nonPowerOfTwoSize() {
64+
int size = 13;
65+
FenwickTree tree = new FenwickTree(size);
66+
for (int i = 0; i < size; i++) {
67+
tree.update(i, 1);
68+
}
69+
for (int i = 0; i < size; i++) {
70+
assertEquals(i + 1, tree.query(i));
71+
}
72+
}
73+
74+
@Test
75+
void matchesNaivePrefixSumOnRandomData() {
76+
Random random = new Random(42);
77+
int size = 100;
78+
FenwickTree tree = new FenwickTree(size);
79+
int[] naive = new int[size];
80+
81+
for (int step = 0; step < 500; step++) {
82+
int index = random.nextInt(size);
83+
int delta = random.nextInt(21) - 10;
84+
tree.update(index, delta);
85+
naive[index] += delta;
86+
}
87+
88+
int running = 0;
89+
for (int i = 0; i < size; i++) {
90+
running += naive[i];
91+
assertEquals(running, tree.query(i), "Mismatch at index " + i);
92+
}
93+
}
94+
95+
@Test
96+
void zeroSizeTreeRejectsAnyIndex() {
97+
FenwickTree tree = new FenwickTree(0);
98+
assertThrows(IndexOutOfBoundsException.class, () -> tree.update(0, 1));
99+
assertThrows(IndexOutOfBoundsException.class, () -> tree.query(0));
100+
}
101+
102+
@Test
103+
void negativeSizeThrows() {
104+
assertThrows(IllegalArgumentException.class, () -> new FenwickTree(-1));
105+
}
106+
107+
@Test
108+
void outOfBoundsIndicesThrow() {
109+
FenwickTree tree = new FenwickTree(5);
110+
assertThrows(IndexOutOfBoundsException.class, () -> tree.update(-1, 1));
111+
assertThrows(IndexOutOfBoundsException.class, () -> tree.update(5, 1));
112+
assertThrows(IndexOutOfBoundsException.class, () -> tree.query(-1));
113+
assertThrows(IndexOutOfBoundsException.class, () -> tree.query(5));
114+
}
115+
}

0 commit comments

Comments
 (0)