惰性传播几乎总是包含某种哨兵机制。您必须验证当前节点是否不需要传播,并且此检查应该简单且快速。所以有两种可能:
- 牺牲一点内存来保存节点中的一个字段,可以很容易地检查该字段
- 牺牲一点运行时间来检查节点是否已传播以及是否必须创建其子节点。
我坚持第一个。检查分段树中的节点是否应该有子节点非常简单(node->lower_value != node->upper_value
),但您还必须检查这些子节点是否已经构建(node->left_child, node->right_child
),所以我引入了一个传播标志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;
初始化
为了初始化一个节点,我们调用initialize
带有指向节点指针的指针(或NULL
)和所需的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;
}
Access
到目前为止,还没有做任何特别的事情。这看起来就像所有其他通用节点创建方法。但是,为了创建实际的子节点并设置传播标志,我们可以使用一个函数,该函数将返回同一节点上的指针,但如果需要则传播它:
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;
}
正如您所看到的,传播的节点几乎会立即退出该函数。未传播的节点将首先检查它是否实际上应包含子节点,然后根据需要创建它们。
这实际上是惰性评估。在需要时才创建子节点。注意accessErr
还提供了额外的错误接口。如果你不需要它使用access
反而:
lazy_segment_node * access(lazy_segment_node* node){
return accessErr(node,NULL);
}
Free
为了释放这些元素,您可以使用通用节点释放算法:
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);
}
完整示例
下面的示例将使用上述函数根据区间 [1,10] 创建惰性求值线段树。可以看到第一次初始化后test
没有子节点。通过使用access
您实际上生成了这些子节点并可以获得它们的值(如果分段树的逻辑存在这些子节点):
Code
#include <stdlib.h>
#include <stdio.h>
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;
}
Result (ideone) http://ideone.com/bNVDx
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