|
| 1 | +/** |
| 2 | + * @file segment_tree.c |
| 3 | + * @brief segment trees with only point updates |
| 4 | + * @details |
| 5 | + * This code implements segment trees. Segment trees are general structures |
| 6 | + * which allow range based queries in a given array in logN time. |
| 7 | + * Segment tree with point updates allow update of single element in the array |
| 8 | + * in logN time. |
| 9 | + * [Learn more about segment trees |
| 10 | + * here](https://codeforces.com/blog/entry/18051) |
| 11 | + * @author [Lakhan Nad](https://github.com/Lakhan-Nad) |
| 12 | + */ |
| 13 | + |
| 14 | +#include <assert.h> /* for assert */ |
| 15 | +#include <inttypes.h> /* for int32 */ |
| 16 | +#include <stdio.h> /* for scanf printf */ |
| 17 | +#include <stdlib.h> /* for malloc, free */ |
| 18 | +#include <string.h> /* for memcpy, memset */ |
| 19 | + |
| 20 | +/** |
| 21 | + * Function that combines two data to generate a new one |
| 22 | + * The name of function might be misleading actually combine here signifies the |
| 23 | + * fact that in segment trees we take partial result from two ranges and using |
| 24 | + * partial results we derive the result for joint range of those two ranges |
| 25 | + * For Example: array(1,2,3,4,5,6) sum of range [0,2] = 6 |
| 26 | + * and sum of range [3,5] = 15 the combined sum of two range is 6+15=21 |
| 27 | + * @note The function is same to binary function in Discrete Mathematics |
| 28 | + * @param a pointer to first data |
| 29 | + * @param b pointer to second data |
| 30 | + * @param result pointer to memory location where result of combining a and b is |
| 31 | + * to be stored |
| 32 | + */ |
| 33 | +typedef void (*combine_function)(const void *a, const void *b, void *result); |
| 34 | + |
| 35 | +/** |
| 36 | + * This structures holds all the data that is required by a segment tree |
| 37 | + */ |
| 38 | +typedef struct segment_tree |
| 39 | +{ |
| 40 | + void *root; /**< the root of formed segment tree */ |
| 41 | + void *identity; /**< identity element for combine function */ |
| 42 | + size_t elem_size; /**< size in bytes of each data element */ |
| 43 | + size_t length; /**< total size of array which segment tree represents*/ |
| 44 | + /** the function to be used to combine two node's |
| 45 | + * data to form parent's data |
| 46 | + */ |
| 47 | + combine_function combine; |
| 48 | +} segment_tree; |
| 49 | + |
| 50 | +/** |
| 51 | + * Builds a Segment tree |
| 52 | + * It is assumed that leaves of tree already contains data. |
| 53 | + * @param tree pointer to segment tree to be build |
| 54 | + */ |
| 55 | +void segment_tree_build(segment_tree *tree) |
| 56 | +{ |
| 57 | + size_t elem_size = tree->elem_size; |
| 58 | + int index = (tree->length - 2); |
| 59 | + size_t b, l, r; |
| 60 | + char *ptr = (char *)tree->root; |
| 61 | + for (; index >= 0; index--) |
| 62 | + { |
| 63 | + b = index * elem_size; |
| 64 | + l = (2 * index + 1) * elem_size; |
| 65 | + r = (2 * index + 2) * elem_size; |
| 66 | + tree->combine(ptr + l, ptr + r, ptr + b); |
| 67 | + } |
| 68 | +} |
| 69 | + |
| 70 | +/** |
| 71 | + * For point updates |
| 72 | + * This function updates the element at given index and also updates segment |
| 73 | + * tree accordingly |
| 74 | + * |
| 75 | + * @param tree pointer to segment tree |
| 76 | + * @param index the index whose element is to be updated (0 based indexing used) |
| 77 | + * @param val pointer to value that is to be replaced at given index |
| 78 | + */ |
| 79 | +void segment_tree_update(segment_tree *tree, size_t index, void *val) |
| 80 | +{ |
| 81 | + size_t elem_size = tree->elem_size; |
| 82 | + index = index + tree->length - 1; |
| 83 | + char *base = (char *)tree->root; |
| 84 | + char *t = base + index * elem_size; |
| 85 | + memcpy(t, val, elem_size); |
| 86 | + while (index > 0) |
| 87 | + { |
| 88 | + index = ((index - 1) >> 1); |
| 89 | + tree->combine(base + (2 * index + 1) * elem_size, |
| 90 | + base + (2 * index + 2) * elem_size, |
| 91 | + base + index * elem_size); |
| 92 | + } |
| 93 | +} |
| 94 | + |
| 95 | +/** |
| 96 | + * Query the segment tree |
| 97 | + * This function helps in range query of segment tree |
| 98 | + * This function assumes that the given range is valid |
| 99 | + * Performs the query in range [l,r] |
| 100 | + * @param tree pointer to segment tree |
| 101 | + * @param l the start of range |
| 102 | + * @param r the end of range |
| 103 | + * @param res the pointer to memory where result of query is stored |
| 104 | + */ |
| 105 | +void segment_tree_query(segment_tree *tree, long long l, long long r, void *res) |
| 106 | +{ |
| 107 | + size_t elem_size = tree->elem_size; |
| 108 | + memcpy(res, tree->identity, elem_size); |
| 109 | + elem_size = tree->elem_size; |
| 110 | + char *root = (char *)tree->root; |
| 111 | + l += tree->length - 1; |
| 112 | + r += tree->length - 1; |
| 113 | + while (l <= r) |
| 114 | + { |
| 115 | + if (!(l & 1)) |
| 116 | + { |
| 117 | + tree->combine(res, root + l * elem_size, res); |
| 118 | + } |
| 119 | + if (r & 1) |
| 120 | + { |
| 121 | + tree->combine(res, root + r * elem_size, res); |
| 122 | + } |
| 123 | + r = (r >> 1) - 1; |
| 124 | + l = (l >> 1); |
| 125 | + } |
| 126 | +} |
| 127 | + |
| 128 | +/** |
| 129 | + * Initializes Segment Tree |
| 130 | + * Accquires memory for segment tree |
| 131 | + * and fill the leaves of segment tree with data from array |
| 132 | + * @param arr the array data upon which segment tree is build |
| 133 | + * @param elem_size size of each element in segment tree |
| 134 | + * @param len total no of elements in array |
| 135 | + * @param identity the identity element for combine_function |
| 136 | + * @param func the combine_function used to build segment tree |
| 137 | + * |
| 138 | + * @returns pointer to sgement tree build |
| 139 | + */ |
| 140 | +segment_tree *segment_tree_init(void *arr, size_t elem_size, size_t len, |
| 141 | + void *identity, combine_function func) |
| 142 | +{ |
| 143 | + segment_tree *tree = malloc(sizeof(segment_tree)); |
| 144 | + tree->elem_size = elem_size; |
| 145 | + tree->length = len; |
| 146 | + tree->combine = func; |
| 147 | + tree->root = malloc(sizeof(char) * elem_size * (2 * len - 1)); |
| 148 | + tree->identity = malloc(sizeof(char) * elem_size); |
| 149 | + char *ptr = (char *)tree->root; |
| 150 | + memset(ptr, 0, (len - 1) * elem_size); // Initializing memory |
| 151 | + ptr = ptr + (len - 1) * elem_size; |
| 152 | + memcpy(ptr, arr, elem_size * len); // copy the leaf nodes i.e. array data |
| 153 | + memcpy(tree->identity, identity, elem_size); // copy identity element |
| 154 | + return tree; |
| 155 | +} |
| 156 | + |
| 157 | +/** |
| 158 | + * Dispose Segment Tree |
| 159 | + * Frees all heap memory accquired by segment tree |
| 160 | + * @param tree pointer to segment tree |
| 161 | + */ |
| 162 | +void segment_tree_dispose(segment_tree *tree) |
| 163 | +{ |
| 164 | + free(tree->root); |
| 165 | + free(tree->identity); |
| 166 | +} |
| 167 | + |
| 168 | +/** |
| 169 | + * Prints the data in segment tree |
| 170 | + * The data should be of int type |
| 171 | + * A utility to print segment tree |
| 172 | + * with data type of int |
| 173 | + * @param tree pointer to segment tree |
| 174 | + */ |
| 175 | +void segment_tree_print_int(segment_tree *tree) |
| 176 | +{ |
| 177 | + char *base = (char *)tree->root; |
| 178 | + size_t i = 0; |
| 179 | + for (; i < 2 * tree->length - 1; i++) |
| 180 | + { |
| 181 | + printf("%d ", *(int *)(base + i * tree->elem_size)); |
| 182 | + } |
| 183 | + printf("\n"); |
| 184 | +} |
| 185 | + |
| 186 | +/** |
| 187 | + * Utility for test |
| 188 | + * A function compare for minimum between two integers |
| 189 | + * This function is used as combine_function for RMQ |
| 190 | + * @param a pointer to integer a |
| 191 | + * @param b pointer to integer b |
| 192 | + * @param c pointer where minimum of a and b is tored as result |
| 193 | + */ |
| 194 | +void minimum(const void *a, const void *b, void *c) |
| 195 | +{ |
| 196 | + *(int *)c = *(int *)a < *(int *)b ? *(int *)a : *(int *)b; |
| 197 | +} |
| 198 | + |
| 199 | +/** |
| 200 | + * Test RMQ |
| 201 | + * Testing Segment tree using |
| 202 | + * Range Minimum Queries |
| 203 | + * @returns void |
| 204 | + */ |
| 205 | +static void test() |
| 206 | +{ |
| 207 | + int32_t arr[10] = {1, 0, 3, 5, 7, 2, 11, 6, -2, 8}; |
| 208 | + int32_t identity = __INT32_MAX__; |
| 209 | + segment_tree *tree = |
| 210 | + segment_tree_init(arr, sizeof(*arr), 10, &identity, minimum); |
| 211 | + segment_tree_build(tree); |
| 212 | + int32_t result; |
| 213 | + segment_tree_query(tree, 3, 6, &result); |
| 214 | + assert(result == 2); |
| 215 | + segment_tree_query(tree, 8, 9, &result); |
| 216 | + assert(result == -2); |
| 217 | + result = 12; |
| 218 | + segment_tree_update(tree, 5, &result); |
| 219 | + segment_tree_update(tree, 8, &result); |
| 220 | + segment_tree_query(tree, 0, 3, &result); |
| 221 | + assert(result == 0); |
| 222 | + segment_tree_query(tree, 8, 9, &result); |
| 223 | + assert(result == 8); |
| 224 | + segment_tree_dispose(tree); |
| 225 | +} |
| 226 | + |
| 227 | +/** |
| 228 | + * @brief Main Function |
| 229 | + * @returns 0 on exit |
| 230 | + */ |
| 231 | +int main() |
| 232 | +{ |
| 233 | + test(); |
| 234 | + return 0; |
| 235 | +} |
0 commit comments