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