mirror of
https://github.com/TheAlgorithms/C
synced 2024-11-25 23:09:36 +03:00
e75d0e75d7
* Feature: Added segment_trees Closes #561 * doc: documentation improved and explanations added * updating DIRECTORY.md * doc: changes in documentation * doc: suggested doc changes and int types changed * update: doc change for main function Co-authored-by: David Leal <halfpacho@gmail.com> Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: David Leal <halfpacho@gmail.com>
236 lines
7.1 KiB
C
236 lines
7.1 KiB
C
/**
|
|
* @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;
|
|
}
|