|
| 1 | +package com.williamfiset.algorithms.datastructures.segmenttree; |
| 2 | + |
| 3 | +import java.util.Arrays; |
| 4 | + |
1 | 5 | /** |
2 | | - * A compact array based segment tree implementation. This segment tree supports point updates and |
3 | | - * range queries. |
| 6 | + * Compact Array-Based Segment Tree |
| 7 | + * |
| 8 | + * A space-efficient segment tree stored in a flat array of size 2*n (no |
| 9 | + * recursion, no pointers). Supports point updates and range queries using |
| 10 | + * any associative combine function (sum, min, max, product, GCD, etc.). |
| 11 | + * |
| 12 | + * The tree is stored bottom-up: leaves occupy indices [n, 2n) and internal |
| 13 | + * nodes occupy [1, n). Index 0 is unused. Each internal node i is the |
| 14 | + * combination of its children at 2i and 2i+1. |
| 15 | + * |
| 16 | + * Use cases: |
| 17 | + * - Range sum / min / max queries with point updates |
| 18 | + * - Competitive programming (very short, cache-friendly implementation) |
| 19 | + * |
| 20 | + * Time: O(n) construction, O(log(n)) per query and update |
| 21 | + * Space: O(n) |
4 | 22 | * |
5 | 23 | * @author Al.Cash & William Fiset, william.alexandre.fiset@gmail.com |
6 | 24 | */ |
7 | | -package com.williamfiset.algorithms.datastructures.segmenttree; |
8 | | - |
9 | 25 | public class CompactSegmentTree { |
10 | 26 |
|
11 | 27 | private int N; |
12 | 28 |
|
13 | | - // Let UNIQUE be a value which does NOT |
14 | | - // and will not appear in the segment tree |
15 | | - private long UNIQUE = 8123572096793136074L; |
16 | | - |
17 | | - // Segment tree values |
18 | | - private long[] tree; |
| 29 | + // Flat array storing the segment tree. Leaves are at indices [N, 2N), |
| 30 | + // internal nodes at [1, N). Index 0 is unused. Uninitialized slots |
| 31 | + // are null, which acts as the identity element for the combine function. |
| 32 | + private Long[] tree; |
19 | 33 |
|
| 34 | + /** |
| 35 | + * Creates an empty segment tree of the given size, with all slots |
| 36 | + * initialized to null. |
| 37 | + * |
| 38 | + * @param size the number of elements (leaves) in the segment tree |
| 39 | + */ |
20 | 40 | public CompactSegmentTree(int size) { |
21 | | - tree = new long[2 * (N = size)]; |
22 | | - java.util.Arrays.fill(tree, UNIQUE); |
| 41 | + tree = new Long[2 * (N = size)]; |
23 | 42 | } |
24 | 43 |
|
| 44 | + /** |
| 45 | + * Creates a segment tree from an array of values. |
| 46 | + * |
| 47 | + * @param values the initial leaf values |
| 48 | + */ |
25 | 49 | public CompactSegmentTree(long[] values) { |
26 | 50 | this(values.length); |
27 | | - // TODO(william): Implement smarter construction. |
28 | 51 | for (int i = 0; i < N; i++) modify(i, values[i]); |
29 | 52 | } |
30 | 53 |
|
31 | | - // This is the segment tree function we are using for queries. |
32 | | - // The function must be an associative function, meaning |
33 | | - // the following property must hold: f(f(a,b),c) = f(a,f(b,c)). |
34 | | - // Common associative functions used with segment trees |
35 | | - // include: min, max, sum, product, GCD, and etc... |
36 | | - private long function(long a, long b) { |
37 | | - if (a == UNIQUE) return b; |
38 | | - else if (b == UNIQUE) return a; |
39 | | - |
40 | | - return a + b; // sum over a range |
41 | | - // return (a > b) ? a : b; // maximum value over a range |
42 | | - // return (a < b) ? a : b; // minimum value over a range |
43 | | - // return a * b; // product over a range (watch out for overflow!) |
| 54 | + /** |
| 55 | + * The associative combine function used for queries. This function must |
| 56 | + * satisfy f(f(a,b), c) = f(a, f(b,c)) for correct segment tree behavior. |
| 57 | + * Null acts as the identity element: f(null, x) = f(x, null) = x. |
| 58 | + * |
| 59 | + * Change this to customize the query type: |
| 60 | + * return a + b; // sum over a range |
| 61 | + * return (a > b) ? a : b; // maximum over a range |
| 62 | + * return (a < b) ? a : b; // minimum over a range |
| 63 | + * return a * b; // product over a range (watch for overflow!) |
| 64 | + */ |
| 65 | + private Long function(Long a, Long b) { |
| 66 | + if (a == null) return b; |
| 67 | + if (b == null) return a; |
| 68 | + return a + b; |
44 | 69 | } |
45 | 70 |
|
46 | | - // Adjust point i by a value, O(log(n)) |
| 71 | + /** |
| 72 | + * Updates the value at index i by combining it with the given value |
| 73 | + * using the combine function, then propagates changes up to the root. |
| 74 | + * |
| 75 | + * @param i the leaf index to update (0-based) |
| 76 | + * @param value the value to combine at position i |
| 77 | + * |
| 78 | + * Time: O(log(n)) |
| 79 | + */ |
47 | 80 | public void modify(int i, long value) { |
| 81 | + // Update the leaf node |
48 | 82 | tree[i + N] = function(tree[i + N], value); |
| 83 | + // Propagate up: recompute each ancestor from its two children |
49 | 84 | for (i += N; i > 1; i >>= 1) { |
50 | 85 | tree[i >> 1] = function(tree[i], tree[i ^ 1]); |
51 | 86 | } |
52 | 87 | } |
53 | 88 |
|
54 | | - // Query interval [l, r), O(log(n)) |
| 89 | + /** |
| 90 | + * Queries the aggregate value over the half-open interval [l, r). |
| 91 | + * |
| 92 | + * Works by starting at the leaves and moving up. At each level, if the |
| 93 | + * left boundary is a right child, include it and move right. If the right |
| 94 | + * boundary is a right child, move left and include it. |
| 95 | + * |
| 96 | + * @param l left endpoint (inclusive, 0-based) |
| 97 | + * @param r right endpoint (exclusive, 0-based) |
| 98 | + * @return the combined result over [l, r) |
| 99 | + * @throws IllegalStateException if the query range is empty |
| 100 | + * |
| 101 | + * Time: O(log(n)) |
| 102 | + */ |
55 | 103 | public long query(int l, int r) { |
56 | | - long res = UNIQUE; |
| 104 | + Long res = null; |
57 | 105 | for (l += N, r += N; l < r; l >>= 1, r >>= 1) { |
| 106 | + // If l is a right child, include it and move to next subtree |
58 | 107 | if ((l & 1) != 0) res = function(res, tree[l++]); |
| 108 | + // If r is a right child, include its left sibling |
59 | 109 | if ((r & 1) != 0) res = function(res, tree[--r]); |
60 | 110 | } |
61 | | - if (res == UNIQUE) { |
62 | | - throw new IllegalStateException("UNIQUE should not be the return value."); |
| 111 | + if (res == null) { |
| 112 | + throw new IllegalStateException("Empty query range."); |
63 | 113 | } |
64 | 114 | return res; |
65 | 115 | } |
66 | 116 |
|
67 | 117 | public static void main(String[] args) { |
68 | | - // exmaple1(); |
69 | | - example2(); |
70 | | - } |
71 | | - |
72 | | - private static void example1() { |
73 | | - long[] values = new long[] {3, 0, 8, 9, 8, 2, 5, 3, 7, 1}; |
74 | | - CompactSegmentTree st = new CompactSegmentTree(values); |
75 | | - System.out.println(java.util.Arrays.toString(st.tree)); |
76 | | - } |
77 | | - |
78 | | - private static void example2() { |
79 | 118 | long[] values = new long[] {1, 1, 1, 1, 1, 1}; |
80 | 119 | CompactSegmentTree st = new CompactSegmentTree(values); |
81 | | - System.out.println(java.util.Arrays.toString(st.tree)); |
82 | | - |
| 120 | + System.out.println(Arrays.toString(st.tree)); |
83 | 121 | System.out.println(st.query(0, 6)); // 6 |
84 | 122 | System.out.println(st.query(1, 5)); // 4 |
85 | 123 | System.out.println(st.query(0, 2)); // 2 |
|
0 commit comments