I have searched on internet about implementation of Segment trees but found nothing when it came to lazy propagation. There were some previous questions on stack overflow but th
Lazy propagation almost always includes some kind of sentry-mechanism. You have to verify that the current node doesn't need to be propagated, and this check should be easy and fast. So there are two possibilities:
I sticked myself to the first. It's very simple to check whether a node in a segmented tree should have child nodes (node->lower_value != node->upper_value
), but you would also have to check whether those child node are already built (node->left_child, node->right_child
), so I introduced a propagation flag node->propagated
:
typedef struct lazy_segment_node{
int lower_value;
int upper_value;
struct lazy_segment_node * left_child;
struct lazy_segment_node * right_child;
unsigned char propagated;
} lazy_segment_node;
To initialize a node we call initialize
with a pointer to the node pointer (or NULL
) and the desired upper_value
/lower_value
:
lazy_segment_node * initialize(
lazy_segment_node ** mem,
int lower_value,
int upper_value
){
lazy_segment_node * tmp = NULL;
if(mem != NULL)
tmp = *mem;
if(tmp == NULL)
tmp = malloc(sizeof(lazy_segment_node));
if(tmp == NULL)
return NULL;
tmp->lower_value = lower_value;
tmp->upper_value = upper_value;
tmp->propagated = 0;
tmp->left_child = NULL;
tmp->right_child = NULL;
if(mem != NULL)
*mem = tmp;
return tmp;
}
So far nothing special has been done. This looks like every other generic node creation method. However, in order to create the actual child nodes and set the propagation flags we can use a function which will return a pointer on the same node, but propagates it if needed:
lazy_segment_node * accessErr(lazy_segment_node* node, int * error){
if(node == NULL){
if(error != NULL)
*error = 1;
return NULL;
}
/* if the node has been propagated already return it */
if(node->propagated)
return node;
/* the node doesn't need child nodes, set flag and return */
if(node->upper_value == node->lower_value){
node->propagated = 1;
return node;
}
/* skipping left and right child creation, see code below*/
return node;
}
As you can see, a propagated node will exit the function almost immediately. A not propagated node will instead first check whether it should actually contain child nodes and then create them if needed.
This is actually the lazy-evaluation. You don't create the child nodes until needed. Note that accessErr
also provides an additional error interface. If you don't need it use access
instead:
lazy_segment_node * access(lazy_segment_node* node){
return accessErr(node,NULL);
}
In order to free those elements you can use a generic node deallocation algorithm:
void free_lazy_segment_tree(lazy_segment_node * root){
if(root == NULL)
return;
free_lazy_segment_tree(root->left_child);
free_lazy_segment_tree(root->right_child);
free(root);
}
The following example will use the functions described above to create a lazy-evaluated segment tree based on the interval [1,10]. You can see that after the first initialization test
has no child nodes. By using access
you actually generate those child nodes and can get their values (if those child nodes exists by the segmented tree's logic):
#include
#include
typedef struct lazy_segment_node{
int lower_value;
int upper_value;
unsigned char propagated;
struct lazy_segment_node * left_child;
struct lazy_segment_node * right_child;
} lazy_segment_node;
lazy_segment_node * initialize(lazy_segment_node ** mem, int lower_value, int upper_value){
lazy_segment_node * tmp = NULL;
if(mem != NULL)
tmp = *mem;
if(tmp == NULL)
tmp = malloc(sizeof(lazy_segment_node));
if(tmp == NULL)
return NULL;
tmp->lower_value = lower_value;
tmp->upper_value = upper_value;
tmp->propagated = 0;
tmp->left_child = NULL;
tmp->right_child = NULL;
if(mem != NULL)
*mem = tmp;
return tmp;
}
lazy_segment_node * accessErr(lazy_segment_node* node, int * error){
if(node == NULL){
if(error != NULL)
*error = 1;
return NULL;
}
if(node->propagated)
return node;
if(node->upper_value == node->lower_value){
node->propagated = 1;
return node;
}
node->left_child = initialize(NULL,node->lower_value,(node->lower_value + node->upper_value)/2);
if(node->left_child == NULL){
if(error != NULL)
*error = 2;
return NULL;
}
node->right_child = initialize(NULL,(node->lower_value + node->upper_value)/2 + 1,node->upper_value);
if(node->right_child == NULL){
free(node->left_child);
if(error != NULL)
*error = 3;
return NULL;
}
node->propagated = 1;
return node;
}
lazy_segment_node * access(lazy_segment_node* node){
return accessErr(node,NULL);
}
void free_lazy_segment_tree(lazy_segment_node * root){
if(root == NULL)
return;
free_lazy_segment_tree(root->left_child);
free_lazy_segment_tree(root->right_child);
free(root);
}
int main(){
lazy_segment_node * test = NULL;
initialize(&test,1,10);
printf("Lazy evaluation test\n");
printf("test->lower_value: %i\n",test->lower_value);
printf("test->upper_value: %i\n",test->upper_value);
printf("\nNode not propagated\n");
printf("test->left_child: %p\n",test->left_child);
printf("test->right_child: %p\n",test->right_child);
printf("\nNode propagated with access:\n");
printf("access(test)->left_child: %p\n",access(test)->left_child);
printf("access(test)->right_child: %p\n",access(test)->right_child);
printf("\nNode propagated with access, but subchilds are not:\n");
printf("access(test)->left_child->left_child: %p\n",access(test)->left_child->left_child);
printf("access(test)->left_child->right_child: %p\n",access(test)->left_child->right_child);
printf("\nCan use access on subchilds:\n");
printf("access(test->left_child)->left_child: %p\n",access(test->left_child)->left_child);
printf("access(test->left_child)->right_child: %p\n",access(test->left_child)->right_child);
printf("\nIt's possible to chain:\n");
printf("access(access(access(test)->right_child)->right_child)->lower_value: %i\n",access(access(access(test)->right_child)->right_child)->lower_value);
printf("access(access(access(test)->right_child)->right_child)->upper_value: %i\n",access(access(access(test)->right_child)->right_child)->upper_value);
free_lazy_segment_tree(test);
return 0;
}
Lazy evaluation test test->lower_value: 1 test->upper_value: 10 Node not propagated test->left_child: (nil) test->right_child: (nil) Node propagated with access: access(test)->left_child: 0x948e020 access(test)->right_child: 0x948e038 Node propagated with access, but subchilds are not: access(test)->left_child->left_child: (nil) access(test)->left_child->right_child: (nil) Can use access on subchilds: access(test->left_child)->left_child: 0x948e050 access(test->left_child)->right_child: 0x948e068 It's possible to chain: access(access(access(test)->right_child)->right_child)->lower_value: 9 access(access(access(test)->right_child)->right_child)->upper_value: 10