Skip to content

Commit 88c532c

Browse files
committed
Added SparseTable
1 parent c6b16fe commit 88c532c

File tree

4 files changed

+409
-4
lines changed

4 files changed

+409
-4
lines changed

src/main/java/com/williamfiset/algorithms/datastructures/segmenttree/CompactSegmentTree.java

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,112 @@
77
package com.williamfiset.algorithms.datastructures.segmenttree;
88

99
public class CompactSegmentTree {
10+
11+
// Observation:
12+
// The ST doesn't lend itself to being traverse top down easily,
13+
// rather, bottom up is nicer in this compact representation.
14+
15+
// Two implementations:
16+
// 1) top down
17+
// 2) bottom up
18+
19+
// public class Pair {
20+
// // Index of the value in the original which derived the best value. The index doesn't
21+
// // always make sense depending on the query type. for example the index makes sense for RMQ
22+
// // but not for sum queries which aggregate values.
23+
// int index;
24+
25+
// // The best value in the range.
26+
// long value;
27+
// public Pair(int index, long value) {
28+
// this.index = index;
29+
// this.value = value;
30+
// }
31+
// public String toString() {
32+
// return "(index = " + index + ", " + value + ")";
33+
// }
34+
// }
35+
36+
// // The number of elements in the original values array.
37+
// private int N;
38+
39+
// // Let UNIQUE be a value which does NOT and will NOT appear in the segment tree.
40+
// // This value is used as a replacement for null to avoid unboxing values.
41+
// //
42+
// // TODO(william): should probably replace the tree with Long[] instead of long[] to remove this...
43+
// private long UNIQUE = 8123572096793136074L;
44+
45+
// // Segment tree range values. This array is 1 based so the first entry tree[0]
46+
// // is unused.
47+
// private long[] tree;
48+
49+
// public CompactSegmentTree(int size) {
50+
// tree = new long[2 * (N = size)];
51+
// java.util.Arrays.fill(tree, UNIQUE);
52+
// }
53+
54+
// public CompactSegmentTree(long[] values) {
55+
// this(values.length);
56+
// for (int i = 0; i < N; i++) modify(i, values[i]);
57+
// }
58+
59+
// // This is the segment tree function we are using for queries.
60+
// // The function must be an associative function, meaning
61+
// // the following property must hold: f(f(a,b),c) = f(a,f(b,c)).
62+
// // Common associative functions used with segment trees
63+
// // include: min, max, sum, product, GCD, and etc...
64+
// private int function(int ai, int bi) {
65+
// if (ai == -1 || tree[ai] == UNIQUE) return bi;
66+
// else if (bi == -1 || tree[bi] == UNIQUE) return ai;
67+
68+
// // return a + b; // sum over a range
69+
// // return (a > b) ? a : b; // maximum value over a range
70+
// if (tree[ai] < tree[bi]) { // minimum value over a range
71+
// return ai;
72+
// } else {
73+
// return bi;
74+
// }
75+
// // return a * b; // product over a range (watch out for overflow!)
76+
// }
77+
78+
// // Adjust point i by a value, O(log(n))
79+
// public void modify(int i, long value) {
80+
// // Setting tree[0] to the value and then using the function is a
81+
// // small hack to see if updating the value makes an improvement.
82+
// tree[0] = value;
83+
// if (function(i + N, 0) == 0) {
84+
// tree[i + N] = value;
85+
// }
86+
// // tree[i + N] = function(tree[i + N], value);
87+
// for (i += N; i > 1; i >>= 1) {
88+
// tree[i >> 1] = tree[function(i, i ^ 1)];
89+
// }
90+
// }
91+
92+
// // Query interval [l, r), O(log(n))
93+
// public Pair query(int l, int r) {
94+
// long res = UNIQUE;
95+
// int resIndex = -1;
96+
97+
// for (l += N, r += N; l < r; l >>= 1, r >>= 1) {
98+
// int bestLeft = -1, bestRight = -1;
99+
// if ((l & 1) != 0) {
100+
// bestLeft = function(resIndex, l++);
101+
// }
102+
// if ((r & 1) != 0) {
103+
// bestRight = function(resIndex, --r);
104+
// }
105+
// resIndex = Math.min(bestLeft, bestRight);
106+
// }
107+
// if (tree[resIndex] == UNIQUE) {
108+
// throw new IllegalStateException("UNIQUE should not be the return value.");
109+
// }
110+
// // TODO(william): I don't think the result index is returning what you think it is returning.
111+
// // Not convinced the leaf node indexes get propagated at all...
112+
// // TODO(william): What about multiple matches, what then?
113+
// return new Pair(resIndex, tree[resIndex]);
114+
// }
115+
10116
private int N;
11117

12118
// Let UNIQUE be a value which does NOT
@@ -23,6 +129,7 @@ public CompactSegmentTree(int size) {
23129

24130
public CompactSegmentTree(long[] values) {
25131
this(values.length);
132+
// TODO(william): Implement smarter construction.
26133
for (int i = 0; i < N; i++) modify(i, values[i]);
27134
}
28135

@@ -61,4 +168,10 @@ public long query(int l, int r) {
61168
}
62169
return res;
63170
}
171+
172+
public static void main(String[] args) {
173+
long[] values = new long[]{3,0,8,9,8,2,5,3,7,1};
174+
CompactSegmentTree st = new CompactSegmentTree(values);
175+
System.out.println(java.util.Arrays.toString(st.tree));
176+
}
64177
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/**
2+
*
3+
*
4+
* https://cp-algorithms.com/data_structures/sparse-table.html
5+
*/
6+
package com.williamfiset.algorithms.datastructures.sparsetable;
7+
8+
import java.util.function.BinaryOperator;
9+
10+
public class SparseTable {
11+
12+
// The number of elements in the original input array.
13+
private int n;
14+
15+
// The maximum power of 2 needed. This value of floor(log2(n))
16+
private int P;
17+
18+
// Fast log2(i) lookup table, 1 <= i <= n
19+
private int[] log2;
20+
21+
// The sprase table.
22+
private int[][] t;
23+
24+
// TODO(william): add GCD after more testing.
25+
public enum Operation {
26+
MIN,
27+
MAX,
28+
SUM
29+
};
30+
31+
private Operation op;
32+
33+
private BinaryOperator<Integer> sumFn = (a, b) -> a + b;
34+
private BinaryOperator<Integer> minFn = (a, b) -> Math.min(a, b);
35+
private BinaryOperator<Integer> maxFn = (a, b) -> Math.max(a, b);
36+
private BinaryOperator<Integer> gcdFn = (a, b) -> {
37+
int gcd = b;
38+
while (b != 0) {
39+
gcd = b;
40+
b = a % b;
41+
a = gcd;
42+
}
43+
return Math.abs(gcd);
44+
};
45+
46+
47+
// Supply an array of values and an associative binary function. The function
48+
// is usually min, max sum, gcd, etc... see pre-made ones below.
49+
public SparseTable(int[] v, Operation op) {
50+
this.op = op;
51+
init(v);
52+
}
53+
54+
private void init(int[] v) {
55+
n = v.length;
56+
P = (int) (Math.log(n)/Math.log(2));
57+
t = new int[P+1][n];
58+
59+
for (int i = 0; i < n; i++) {
60+
t[0][i] = v[i];
61+
}
62+
63+
log2 = new int[n+1];
64+
for (int i = 2; i <= n; i++) {
65+
log2[i] = log2[i/2] + 1;
66+
}
67+
68+
System.out.println("Log table: ");
69+
System.out.println(java.util.Arrays.toString(log2));
70+
71+
// O(nlog(n))
72+
for (int p = 1; p <= P; p++) {
73+
for (int i = 0; i + (1 << p) <= n; i++) {
74+
if (op == Operation.SUM) {
75+
t[p][i] = sumFn.apply(t[p-1][i], t[p-1][i + (1 << (p-1))]);
76+
} else if (op == Operation.MIN) {
77+
t[p][i] = minFn.apply(t[p-1][i], t[p-1][i + (1 << (p-1))]);
78+
} else if (op == Operation.MAX) {
79+
t[p][i] = maxFn.apply(t[p-1][i], t[p-1][i + (1 << (p-1))]);
80+
}
81+
}
82+
}
83+
84+
System.out.println();
85+
print(t);
86+
}
87+
88+
public int query(int l, int r) {
89+
if (op == Operation.MIN) {
90+
return minQuery(l, r);
91+
} else if (op == Operation.MAX) {
92+
return maxQuery(l, r);
93+
}
94+
return sumQuery(l, r);
95+
}
96+
97+
// Do sum query [l, r] in O(lg(n))
98+
public int sumQuery(int l, int r) {
99+
int sum = 0;
100+
for (int p = P; p >= 0; p--) {
101+
// System.out.println(l + " " + r);
102+
int rangeLength = r - l + 1;
103+
if ((1 << p) <= rangeLength) {
104+
sum += t[p][l];
105+
l += (1 << p);
106+
}
107+
}
108+
return sum;
109+
}
110+
111+
// Do min query [l, r] in O(1)
112+
private int minQuery(int l, int r) {
113+
int p = log2[r - l + 1];
114+
return Math.min(t[p][l], t[p][r - (1 << p) + 1]);
115+
}
116+
117+
// Do max query [l, r] in O(1)
118+
private int maxQuery(int l, int r) {
119+
int p = log2[r - l + 1];
120+
return Math.max(t[p][l], t[p][r - (1 << p) + 1]);
121+
}
122+
123+
private void print(int[][] t) {
124+
for (int[] r : t) {
125+
System.out.println(java.util.Arrays.toString(r));
126+
}
127+
System.out.println();
128+
}
129+
130+
public static void main(String[] args) {
131+
int[] v = {1,8,5,6,7,2,7,1,8,5,6,7,2,4,3};
132+
System.out.println(v.length);
133+
SparseTable st = new SparseTable(v, Operation.MIN);
134+
}
135+
136+
}
137+

src/main/java/com/williamfiset/algorithms/graphtheory/treealgorithms/LowestCommonAncestor.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,6 @@ private boolean helper(TreeNode node, int id1, int id2) {
9696
return count > 0;
9797
}
9898

99-
private static boolean match(TreeNode node, int id1, int id2) {
100-
return node != null && (node.id() == id1 || node.id() == id2);
101-
}
102-
10399
/* Graph/Tree creation helper methods. */
104100

105101
// Create a graph as a adjacency list with 'n' nodes.

0 commit comments

Comments
 (0)