Skip to content

Commit df08e57

Browse files
committed
Add sprase table to README
1 parent e45d7fe commit df08e57

File tree

2 files changed

+35
-55
lines changed

2 files changed

+35
-55
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ or
104104
* [Segment tree (array based, compact)](https://github.com/williamfiset/algorithms/tree/master/src/main/java/com/williamfiset/algorithms/datastructures/segmenttree/CompactSegmentTree.java)
105105
* [Segment tree (pointer implementation)](https://github.com/williamfiset/algorithms/tree/master/src/main/java/com/williamfiset/algorithms/datastructures/segmenttree/Node.java)
106106
* [Skip List [UNTESTED]](https://github.com/williamfiset/algorithms/tree/master/src/main/java/com/williamfiset/algorithms/datastructures/skiplist/SkipList.java)
107+
* [Sparse Table](https://github.com/williamfiset/algorithms/tree/master/src/main/java/com/williamfiset/algorithms/datastructures/sparsetable/SparseTable.java)
107108
* [:movie_camera:](https://www.youtube.com/watch?v=L3ud3rXpIxA)[Stack](https://github.com/williamfiset/algorithms/tree/master/src/main/java/com/williamfiset/algorithms/datastructures/stack)
108109
* [Stack (integer only, fixed size, fast)](https://github.com/williamfiset/algorithms/tree/master/src/main/java/com/williamfiset/algorithms/datastructures/stack/IntStack.java)
109110
* [Stack (linked list, generic)](https://github.com/williamfiset/algorithms/tree/master/src/main/java/com/williamfiset/algorithms/datastructures/stack/Stack.java)

src/main/java/com/williamfiset/algorithms/datastructures/sparsetable/SparseTable.java

Lines changed: 34 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
*
55
* <p>Main inspiration: https://cp-algorithms.com/data_structures/sparse-table.html
66
*
7+
* <p>To run this file:
8+
*
9+
* <p>./gradlew run -Pmain=com.williamfiset.algorithms.datastructures.sparsetable.SparseTable
10+
*
711
* @author William Fiset, [email protected]
812
*/
913
package com.williamfiset.algorithms.datastructures.sparsetable;
@@ -22,7 +26,7 @@ public class SparseTable {
2226
private int[] log2;
2327

2428
// The sprase table values.
25-
private long[][] t;
29+
private long[][] dp;
2630

2731
// Index Table (IT) associated with the values in the sparse table.
2832
private int[][] it;
@@ -53,18 +57,19 @@ public enum Operation {
5357
};
5458

5559
public SparseTable(long[] values, Operation op) {
60+
// TODO(william): Lazily call init in query methods instead of initializing in constructor?
5661
this.op = op;
5762
init(values);
5863
}
5964

6065
private void init(long[] v) {
6166
n = v.length;
6267
P = (int) (Math.log(n) / Math.log(2));
63-
t = new long[P + 1][n];
68+
dp = new long[P + 1][n];
6469
it = new int[P + 1][n];
6570

6671
for (int i = 0; i < n; i++) {
67-
t[0][i] = v[i];
72+
dp[0][i] = v[i];
6873
it[0][i] = i;
6974
}
7075

@@ -76,27 +81,28 @@ private void init(long[] v) {
7681
// Build sparse table combining the values of the previous intervals.
7782
for (int p = 1; p <= P; p++) {
7883
for (int i = 0; i + (1 << p) <= n; i++) {
79-
long leftInterval = t[p - 1][i], rightInterval = t[p - 1][i + (1 << (p - 1))];
84+
long leftInterval = dp[p - 1][i];
85+
long rightInterval = dp[p - 1][i + (1 << (p - 1))];
8086
if (op == Operation.MIN) {
81-
t[p][i] = minFn.apply(leftInterval, rightInterval);
87+
dp[p][i] = minFn.apply(leftInterval, rightInterval);
8288
// Propagate the index of the best value
8389
if (leftInterval <= rightInterval) {
8490
it[p][i] = it[p - 1][i];
8591
} else {
8692
it[p][i] = it[p - 1][i + (1 << (p - 1))];
8793
}
8894
} else if (op == Operation.MAX) {
89-
t[p][i] = maxFn.apply(leftInterval, rightInterval);
95+
dp[p][i] = maxFn.apply(leftInterval, rightInterval);
9096
// Propagate the index of the best value
9197
if (leftInterval >= rightInterval) {
9298
it[p][i] = it[p - 1][i];
9399
} else {
94100
it[p][i] = it[p - 1][i + (1 << (p - 1))];
95101
}
96102
} else if (op == Operation.SUM) {
97-
t[p][i] = sumFn.apply(leftInterval, rightInterval);
103+
dp[p][i] = sumFn.apply(leftInterval, rightInterval);
98104
} else if (op == Operation.GCD) {
99-
t[p][i] = gcdFn.apply(leftInterval, rightInterval);
105+
dp[p][i] = gcdFn.apply(leftInterval, rightInterval);
100106
}
101107
}
102108
}
@@ -127,8 +133,8 @@ public int queryIndex(int l, int r) {
127133
private int minQueryIndex(int l, int r) {
128134
int len = r - l + 1;
129135
int p = log2[r - l + 1];
130-
long leftInterval = t[p][l];
131-
long rightInterval = t[p][r - (1 << p) + 1];
136+
long leftInterval = dp[p][l];
137+
long rightInterval = dp[p][r - (1 << p) + 1];
132138
if (leftInterval <= rightInterval) {
133139
return it[p][l];
134140
} else {
@@ -139,8 +145,8 @@ private int minQueryIndex(int l, int r) {
139145
private int maxQueryIndex(int l, int r) {
140146
int len = r - l + 1;
141147
int p = log2[r - l + 1];
142-
long leftInterval = t[p][l];
143-
long rightInterval = t[p][r - (1 << p) + 1];
148+
long leftInterval = dp[p][l];
149+
long rightInterval = dp[p][r - (1 << p) + 1];
144150
if (leftInterval >= rightInterval) {
145151
return it[p][l];
146152
} else {
@@ -153,14 +159,16 @@ private int maxQueryIndex(int l, int r) {
153159
// Perform a cascading query which shrinks the left endpoint while summing over all the intervals
154160
// which are powers of 2 between [l, r].
155161
//
156-
// NOTE: You can achieve a faster time complexity with less memory using a simple prefix array...
157162
// WARNING: This method can easily produces values that overflow.
163+
//
164+
// NOTE: You can achieve a faster time complexity and use less memory with a simple prefix sum
165+
// array. This method is here more as a proof of concept than for its usefulness.
158166
private long sumQuery(int l, int r) {
159167
long sum = 0;
160168
for (int p = P; p >= 0; p--) {
161169
int rangeLength = r - l + 1;
162170
if ((1 << p) <= rangeLength) {
163-
sum += t[p][l];
171+
sum += dp[p][l];
164172
l += (1 << p);
165173
}
166174
}
@@ -173,55 +181,26 @@ private long sumQuery(int l, int r) {
173181
// which we'll call k. Then we can query the intervals [l, l+k] and [r-k+1, r] (which likely
174182
// overlap) and apply the function again. Some functions (like min and max) don't care about
175183
// overlapping intervals so this trick works, but for a function like sum this would return the
176-
// wrong result.
184+
// wrong result since it is not an idempotent binary function.
177185
private long query(int l, int r, BinaryOperator<Long> fn) {
178186
int len = r - l + 1;
179187
int p = log2[r - l + 1];
180-
return fn.apply(t[p][l], t[p][r - (1 << p) + 1]);
188+
return fn.apply(dp[p][l], dp[p][r - (1 << p) + 1]);
181189
}
182190

191+
/* Example usage: */
192+
183193
public static void main(String[] args) {
184-
long[] v = {2, -3, 4, 1, 0, -1, 5, 6};
185-
SparseTable st = new SparseTable(v, Operation.MIN);
186-
System.out.println(st.query(2, 7));
187-
System.out.println(st.queryIndex(2, 7));
194+
long[] values = {2, -3, 4, 1, 0, -1, -1, 5, 6};
188195

189-
simpleTest();
190-
simpleMinQueryTest();
191-
}
196+
// Initialize sparse table to do range minimum queries.
197+
SparseTable sparseTable = new SparseTable(values, Operation.MIN);
192198

193-
private static void simpleMinQueryTest() {
194-
long[] v = {2, -3, 4, 1, 0, -1, 5, 6};
195-
SparseTable st = new SparseTable(v, Operation.MIN);
196-
for (int i = 0; i < v.length; i++) {
197-
for (int j = i; j < v.length; j++) {
198-
long m = Long.MAX_VALUE;
199-
for (int k = i; k <= j; k++) {
200-
m = Math.min(m, v[k]);
201-
}
202-
if (st.query(i, j) != m) {
203-
System.out.println("Sparse table value incorrect.");
204-
}
205-
if (v[st.queryIndex(i, j)] != m) {
206-
System.out.println("SparseTable index incorrect. Got index: " + st.queryIndex(i, j));
207-
}
208-
}
209-
}
210-
}
199+
// Prints: "Min value between [2, 7] = -1"
200+
System.out.printf("Min value between [2, 7] = %d\n", sparseTable.query(2, 7));
211201

212-
private static void simpleTest() {
213-
long[] v = {2, -3, 4, 1, 0, -1, 5, 6};
214-
SparseTable st = new SparseTable(v, Operation.SUM);
215-
for (int i = 0; i < v.length; i++) {
216-
for (int j = i; j < v.length; j++) {
217-
long trueSum = 0;
218-
for (int k = i; k <= j; k++) {
219-
trueSum += v[k];
220-
}
221-
if (st.query(i, j) != trueSum) {
222-
System.out.printf("Ooopse, got %d instead of %d!\n", st.query(i, j), trueSum);
223-
}
224-
}
225-
}
202+
// Prints: "Index of min value between [2, 7] = 5". Returns the leftmost index in the
203+
// event that there are duplicates.
204+
System.out.printf("Index of min value between [2, 7] = %d\n", sparseTable.queryIndex(2, 7));
226205
}
227206
}

0 commit comments

Comments
 (0)