如何实现具有延迟传播的分段树?

时间:2012-08-01 19:05:43

标签: c algorithm data-structures tree

我在互联网上搜索了关于Segment树的实现但是在懒惰传播方面没有找到任何结果。之前有一些关于堆栈溢出的问题,但他们专注于解决SPOJ的一些特殊问题。虽然我认为这是具有伪代码的分段树的最佳解释,但我需要使用延迟传播来实现它。我找到了以下链接:

http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=lowestCommonAncestor#Segment_Trees

除了上述链接之外,还有一些博客,但他们都提到了相同的帖子。

实施例

这个数据结构的应用程序的示例就像是,我已经获得了从1到n的一系列数字。现在我执行一些操作,例如向特定范围添加一些常数或从特定范围中减去一些常数。执行操作后,我应该告诉给定数字中的最小和最大数字。

明显的解决方案将逐个对给定范围内的每个数字执行加法或减法。但是,在没有执行任何操作的情况下,这是不可行的。

更好的方法将使用具有延迟传播技术的Segment Trees。它表示不是单独对每个数字执行更新操作,而是跟踪所有操作,直到完成所有操作。然后最后执行更新操作以获得范围内的最小和最大数字。

包含实际数据的示例

假设我给出范围[1,10],这意味着数字是1,2,3,4,5,6,7,8,9,10。 现在假设我执行的操作将范围[3,6]中的数字减少4,所以现在数字看起来像1,2,-1,0,1,2,7,8,9,10。 现在我执行另一个操作,将范围[5,9]中的数字增加1,因此数字现在看起来像1,2,-1,0,2,3,8,9,10,10。

现在,如果我要求您告诉我最大和最小数字,那么答案将是:

Maximum = 10

Minimum = -1

这只是一个简单的例子。实际问题可能包含数千个这样的加法/减法操作。我希望它现在清楚了。

这是我目前所理解的,但我想互联网上没有统一的链接,可以更好地解释概念和实施。

任何人都可以给出一些很好的解释,包括用于段树中延迟传播的伪代码吗?

感谢。

5 个答案:

答案 0 :(得分:19)

懒惰传播几乎总是包含某种哨兵机制。您必须验证当前节点不需要传播,并且此检查应该简单快速。所以有两种可能性:

  1. 牺牲一点内存来保存节点中的字段,可以很容易地检查
  2. 牺牲一点运行时间来检查节点是否已被传播以及是否必须创建其子节点。
  3. 我坚持第一个。检查分段树中的节点是否应该有子节点(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;
    }
    

    访问

    到目前为止,还没有做任何特别的事情。这看起来像每个其他通用节点创建方法。但是,为了创建实际的子节点并设置传播标志,我们可以使用一个函数,该函数将在同一节点上返回一个指针,但如果需要则传播它:

    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);
    }
    

    为了释放这些元素,您可以使用通用节点释放算法:

    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,您实际生成了这些子节点,并且可以获取它们的值(如果这些子节点存在于分段树的逻辑中):

    代码

    #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;
    }
    

    结果(ideone)

    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

答案 1 :(得分:2)

如果有人在不使用结构的情况下寻找更简单的延迟传播代码:

(代码不言自明)

/**
 * In this code we have a very large array called arr, and very large set of operations
 * Operation #1: Increment the elements within range [i, j] with value val
 * Operation #2: Get max element within range [i, j]
 * Build tree: build_tree(1, 0, N-1)
 * Update tree: update_tree(1, 0, N-1, i, j, value)
 * Query tree: query_tree(1, 0, N-1, i, j)
 */

#include<iostream>
#include<algorithm>
using namespace std;

#include<string.h>
#include<math.h> 

#define N 20
#define MAX (1+(1<<6)) // Why? :D
#define inf 0x7fffffff

int arr[N];
int tree[MAX];
int lazy[MAX];

/**
 * Build and init tree
 */
void build_tree(int node, int a, int b) {
    if(a > b) return; // Out of range

    if(a == b) { // Leaf node
            tree[node] = arr[a]; // Init value
        return;
    }

    build_tree(node*2, a, (a+b)/2); // Init left child
    build_tree(node*2+1, 1+(a+b)/2, b); // Init right child

    tree[node] = max(tree[node*2], tree[node*2+1]); // Init root value
}

/**
 * Increment elements within range [i, j] with value value
 */
void update_tree(int node, int a, int b, int i, int j, int value) {

    if(lazy[node] != 0) { // This node needs to be updated
        tree[node] += lazy[node]; // Update it

        if(a != b) {
            lazy[node*2] += lazy[node]; // Mark child as lazy
                lazy[node*2+1] += lazy[node]; // Mark child as lazy
        }

        lazy[node] = 0; // Reset it
    }

    if(a > b || a > j || b < i) // Current segment is not within range [i, j]
        return;

    if(a >= i && b <= j) { // Segment is fully within range
            tree[node] += value;

        if(a != b) { // Not leaf node
            lazy[node*2] += value;
            lazy[node*2+1] += value;
        }

            return;
    }

    update_tree(node*2, a, (a+b)/2, i, j, value); // Updating left child
    update_tree(1+node*2, 1+(a+b)/2, b, i, j, value); // Updating right child

    tree[node] = max(tree[node*2], tree[node*2+1]); // Updating root with max value
}

/**
 * Query tree to get max element value within range [i, j]
 */
int query_tree(int node, int a, int b, int i, int j) {

    if(a > b || a > j || b < i) return -inf; // Out of range

    if(lazy[node] != 0) { // This node needs to be updated
        tree[node] += lazy[node]; // Update it

        if(a != b) {
            lazy[node*2] += lazy[node]; // Mark child as lazy
            lazy[node*2+1] += lazy[node]; // Mark child as lazy
        }

        lazy[node] = 0; // Reset it
    }

    if(a >= i && b <= j) // Current segment is totally within range [i, j]
        return tree[node];

    int q1 = query_tree(node*2, a, (a+b)/2, i, j); // Query left child
    int q2 = query_tree(1+node*2, 1+(a+b)/2, b, i, j); // Query right child

    int res = max(q1, q2); // Return final result

    return res;
}

int main() {
    for(int i = 0; i < N; i++) arr[i] = 1;

    build_tree(1, 0, N-1);

    memset(lazy, 0, sizeof lazy);

    update_tree(1, 0, N-1, 0, 6, 5); // Increment range [0, 6] by 5
    update_tree(1, 0, N-1, 7, 10, 12); // Incremenet range [7, 10] by 12
    update_tree(1, 0, N-1, 10, N-1, 100); // Increment range [10, N-1] by 100

    cout << query_tree(1, 0, N-1, 0, N-1) << endl; // Get max element in range [0, N-1]
}

请参阅此链接以获取更多解释Segment Trees and lazy propagation

答案 2 :(得分:1)

虽然我还没有成功解决它,但我相信这个问题比我们想象的容易得多。您可能甚至不需要使用段树/间隔树...实际上,我尝试了两种实现Segment Tree的方法,一种使用树结构而另一种使用数组,两种解决方案都很快得到了TLE。我觉得可以用Greedy来完成,但我还不确定。无论如何,如果你想看看如何使用Segment Tree完成任务,请随时研究我的解决方案。请注意,max_tree[1]min_tree[1]max/min对应。

#include <iostream>
#include <iomanip>
#include <vector>
#include <string>
#include <algorithm>
#include <map>
#include <set>
#include <utility>
#include <stack>
#include <deque>
#include <queue>
#include <fstream>
#include <functional>
#include <numeric>

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cassert>

#ifdef _WIN32 || _WIN64
#define getc_unlocked _fgetc_nolock
#endif

using namespace std;

const int MAX_RANGE = 1000000;
const int NIL = -(1 << 29);
int data[MAX_RANGE] = {0};
int min_tree[3 * MAX_RANGE + 1];
int max_tree[3 * MAX_RANGE + 1];
int added_to_interval[3 * MAX_RANGE + 1];

struct node {
    int max_value;
    int min_value;
    int added;
    node *left;
    node *right;
};

node* build_tree(int l, int r, int values[]) {
    node *root = new node;
    root->added = 0;
    if (l > r) {
        return NULL;
    }
    else if (l == r) {
        root->max_value = l + 1; // or values[l]
        root->min_value = l + 1; // or values[l]
        root->added = 0;
        root->left = NULL;
        root->right = NULL;
        return root;
    }
    else {  
        root->left = build_tree(l, (l + r) / 2, values);
        root->right = build_tree((l + r) / 2 + 1, r, values);
        root->max_value = max(root->left->max_value, root->right->max_value);
        root->min_value = min(root->left->min_value, root->right->min_value);
        root->added = 0;
        return root;
    }
}

node* build_tree(int l, int r) {
    node *root = new node;
    root->added = 0;
    if (l > r) {
        return NULL;
    }
    else if (l == r) {
        root->max_value = l + 1; // or values[l]
        root->min_value = l + 1; // or values[l]
        root->added = 0;
        root->left = NULL;
        root->right = NULL;
        return root;
    }
    else {  
        root->left = build_tree(l, (l + r) / 2);
        root->right = build_tree((l + r) / 2 + 1, r);
        root->max_value = max(root->left->max_value, root->right->max_value);
        root->min_value = min(root->left->min_value, root->right->min_value);
        root->added = 0;
        return root;
    }
}

void update_tree(node* root, int begin, int end, int i, int j, int amount) {
    // out of range
    if (begin > end || begin > j || end < i) {
        return;
    }
    // in update range (i, j)
    else if (i <= begin && end <= j) {
        root->max_value += amount;
        root->min_value += amount;
        root->added += amount;
    }
    else {
        if (root->left == NULL && root->right == NULL) {
            root->max_value = root->max_value + root->added;
            root->min_value = root->min_value + root->added;
        }
        else if (root->right != NULL && root->left == NULL) {
            update_tree(root->right, (begin + end) / 2 + 1, end, i, j, amount);
            root->max_value = root->right->max_value + root->added;
            root->min_value = root->right->min_value + root->added;
        }
        else if (root->left != NULL && root->right == NULL) {
            update_tree(root->left, begin, (begin + end) / 2, i, j, amount);
            root->max_value = root->left->max_value + root->added;
            root->min_value = root->left->min_value + root->added;
        }
        else {
            update_tree(root->right, (begin + end) / 2 + 1, end, i, j, amount);
            update_tree(root->left, begin, (begin + end) / 2, i, j, amount);
            root->max_value = max(root->left->max_value, root->right->max_value) + root->added;
            root->min_value = min(root->left->min_value, root->right->min_value) + root->added;
        }
    }
}

void print_tree(node* root) {
    if (root != NULL) {
        print_tree(root->left);
        cout << "\t(max, min): " << root->max_value << ", " << root->min_value << endl;
        print_tree(root->right);
    }
}

void clean_up(node*& root) {
    if (root != NULL) {
        clean_up(root->left);
        clean_up(root->right);
        delete root;
        root = NULL;
    }
}

void update_bruteforce(int x, int y, int z, int &smallest, int &largest, int data[], int n) {
    for (int i = x; i <= y; ++i) {
        data[i] += z;       
    }

    // update min/max
    smallest = data[0];
    largest = data[0];
    for (int i = 0; i < n; ++i) {
        if (data[i] < smallest) {
            smallest = data[i];
        }

        if (data[i] > largest) {
            largest = data[i];
        }
    }
}

void build_tree_as_array(int position, int left, int right) {
    if (left > right) {
        return;
    }
    else if (left == right) {
        max_tree[position] = left + 1;
        min_tree[position] = left + 1;
        added_to_interval[position] = 0;
        return;
    }
    else {
        build_tree_as_array(position * 2, left, (left + right) / 2);
        build_tree_as_array(position * 2 + 1, (left + right) / 2 + 1, right);
        max_tree[position] = max(max_tree[position * 2], max_tree[position * 2 + 1]);
        min_tree[position] = min(min_tree[position * 2], min_tree[position * 2 + 1]);
    }
}

void update_tree_as_array(int position, int b, int e, int i, int j, int value) {
    if (b > e || b > j || e < i) {
        return;
    }
    else if (i <= b && e <= j) {
        max_tree[position] += value;
        min_tree[position] += value;
        added_to_interval[position] += value;
        return;
    }
    else {
        int left_branch = 2 * position;
        int right_branch = 2 * position + 1;
        // make sure the array is ok
        if (left_branch >= 2 * MAX_RANGE + 1 || right_branch >= 2 * MAX_RANGE + 1) {
            max_tree[position] = max_tree[position] + added_to_interval[position];
            min_tree[position] = min_tree[position] + added_to_interval[position];
            return;
        }
        else if (max_tree[left_branch] == NIL && max_tree[right_branch] == NIL) {
            max_tree[position] = max_tree[position] + added_to_interval[position];
            min_tree[position] = min_tree[position] + added_to_interval[position];
            return;
        }
        else if (max_tree[left_branch] != NIL && max_tree[right_branch] == NIL) {
            update_tree_as_array(left_branch, b , (b + e) / 2 , i, j, value);
            max_tree[position] = max_tree[left_branch] + added_to_interval[position];
            min_tree[position] = min_tree[left_branch] + added_to_interval[position];
        }
        else if (max_tree[right_branch] != NIL && max_tree[left_branch] == NIL) {
            update_tree_as_array(right_branch, (b + e) / 2 + 1 , e , i, j, value);
            max_tree[position] = max_tree[right_branch] + added_to_interval[position];
            min_tree[position] = min_tree[right_branch] + added_to_interval[position];
        }
        else {
            update_tree_as_array(left_branch, b, (b + e) / 2 , i, j, value);
            update_tree_as_array(right_branch, (b + e) / 2 + 1 , e , i, j, value);
            max_tree[position] = max(max_tree[position * 2], max_tree[position * 2 + 1]) + added_to_interval[position]; 
            min_tree[position] = min(min_tree[position * 2], min_tree[position * 2 + 1]) + added_to_interval[position];
        }
    }
}

void show_data(int data[], int n) {
    cout << "[current data]\n";
    for (int i = 0; i < n; ++i) {
        cout << data[i] << ", ";
    }
    cout << endl;
}

inline void input(int* n) {
    char c = 0;
    while (c < 33) {
        c = getc_unlocked(stdin);
    }

    *n = 0;
    while (c > 33) {
        *n = (*n * 10) + c - '0';
        c = getc_unlocked(stdin);
    }
}

void handle_special_case(int m) {
    int type;
    int x;
    int y;
    int added_amount;
    for (int i = 0; i < m; ++i) {
        input(&type);
        input(&x);
        input(&y);
        input(&added_amount);
    }
    printf("0\n");
}

void find_largest_range_use_tree() {
    int n;
    int m;
    int type;
    int x;
    int y;
    int added_amount;

    input(&n);
    input(&m);

    if (n == 1) {
        handle_special_case(m);
        return;
    }

    node *root = build_tree(0, n - 1);
    for (int i = 0; i < m; ++i) {
        input(&type);
        input(&x);
        input(&y);
        input(&added_amount);
        if (type == 1) {    
            added_amount *= 1;
        }
        else {
            added_amount *= -1;
        }

        update_tree(root, 0, n - 1, x - 1, y - 1, added_amount);
    }

    printf("%d\n", root->max_value - root->min_value);
}

void find_largest_range_use_array() {
    int n;
    int m;
    int type;
    int x;
    int y;
    int added_amount;

    input(&n);
    input(&m);

    if (n == 1) {
        handle_special_case(m);
        return;
    }

    memset(min_tree, NIL, 3 * sizeof(int) * n + 1);
    memset(max_tree, NIL, 3 * sizeof(int) * n + 1);
    memset(added_to_interval, 0, 3 * sizeof(int) * n + 1);
    build_tree_as_array(1, 0, n - 1);

    for (int i = 0; i < m; ++i) {
        input(&type);
        input(&x);
        input(&y);
        input(&added_amount);
        if (type == 1) {    
            added_amount *= 1;
        }
        else {
            added_amount *= -1;
        }

        update_tree_as_array(1, 0, n - 1, x - 1, y - 1, added_amount);
    }

    printf("%d\n", max_tree[1] - min_tree[1]);
}

void update_slow(int x, int y, int value) {
    for (int i = x - 1; i < y; ++i) {
        data[i] += value;
    }
}

void find_largest_range_use_common_sense() {
    int n;
    int m;
    int type;
    int x;
    int y;
    int added_amount;

    input(&n);
    input(&m);

    if (n == 1) {
        handle_special_case(m);
        return;
    }

    memset(data, 0, sizeof(int) * n);
    for (int i = 0; i < m; ++i) {
        input(&type);
        input(&x);
        input(&y);
        input(&added_amount);

        if (type == 1) {    
            added_amount *= 1;
        }
        else {
            added_amount *= -1;
        }

        update_slow(x, y, added_amount);
    }

     // update min/max
    int smallest = data[0] + 1;
    int largest = data[0] + 1;
    for (int i = 1; i < n; ++i) {
        if (data[i] + i + 1 < smallest) {
            smallest = data[i] + i + 1;
        }

        if (data[i] + i + 1 > largest) {
            largest = data[i] + i + 1;
        }
    }

    printf("%d\n", largest - smallest); 
}

void inout_range_of_data() {
    int test_cases;
    input(&test_cases);

    while (test_cases--) {
        find_largest_range_use_common_sense();
    }
}

namespace unit_test {
    void test_build_tree() {
        for (int i = 0; i < MAX_RANGE; ++i) {
            data[i] = i + 1;
        }

        node *root = build_tree(0, MAX_RANGE - 1, data);
        print_tree(root);
    }

    void test_against_brute_force() {
          // arrange
        int number_of_operations = 100;
        for (int i = 0; i < MAX_RANGE; ++i) {
            data[i] = i + 1;
        }

        node *root = build_tree(0, MAX_RANGE - 1, data);

        // print_tree(root);
        // act
        int operation;
        int x;
        int y;
        int added_amount;
        int smallest = 1;
        int largest = MAX_RANGE;

        // assert
        while (number_of_operations--) {
            operation = rand() % 2; 
            x = 1 + rand() % MAX_RANGE;
            y = x + (rand() % (MAX_RANGE - x + 1));
            added_amount = 1 + rand() % MAX_RANGE;
            // cin >> operation >> x >> y >> added_amount;
            if (operation == 1) {
                added_amount *= 1;
            }
            else {
                added_amount *= -1;    
            }

            update_bruteforce(x - 1, y - 1, added_amount, smallest, largest, data, MAX_RANGE);
            update_tree(root, 0, MAX_RANGE - 1, x - 1, y - 1, added_amount);
            assert(largest == root->max_value);
            assert(smallest == root->min_value);
            for (int i = 0; i < MAX_RANGE; ++i) {
                cout << data[i] << ", ";
            }
            cout << endl << endl;
            cout << "correct:\n";
            cout << "\t largest = " << largest << endl;
            cout << "\t smallest = " << smallest << endl;
            cout << "testing:\n";
            cout << "\t largest = " << root->max_value << endl;
            cout << "\t smallest = " << root->min_value << endl;
            cout << "testing:\n";
            cout << "\n------------------------------------------------------------\n";
            cout << "final result: " << largest - smallest << endl;
            cin.get();
        }

        clean_up(root);
    }

    void test_automation() {
          // arrange
        int test_cases;
        int number_of_operations = 100;
        int n;


        test_cases = 10000;
        for (int i = 0; i < test_cases; ++i) {
            n = i + 1;

            int operation;
            int x;
            int y;
            int added_amount;
            int smallest = 1;
            int largest = n;


            // initialize data for brute-force
            for (int i = 0; i < n; ++i) {
                data[i] = i + 1;
            }

            // build tree   
            node *root = build_tree(0, n - 1, data);
            for (int i = 0; i < number_of_operations; ++i) {
                operation = rand() % 2; 
                x = 1 + rand() % n;
                y = x + (rand() % (n - x + 1));
                added_amount = 1 + rand() % n;

                if (operation == 1) {
                    added_amount *= 1;
                }
                else {
                    added_amount *= -1;    
                }

                update_bruteforce(x - 1, y - 1, added_amount, smallest, largest, data, n);
                update_tree(root, 0, n - 1, x - 1, y - 1, added_amount);
                assert(largest == root->max_value);
                assert(smallest == root->min_value);

                cout << endl << endl;
                cout << "For n = " << n << endl;
                cout << ", where data is : \n";
                for (int i = 0; i < n; ++i) {
                    cout << data[i] << ", ";
                }
                cout << endl;
                cout << " and query is " << x - 1 << ", " << y - 1 << ", " << added_amount << endl;
                cout << "correct:\n";
                cout << "\t largest = " << largest << endl;
                cout << "\t smallest = " << smallest << endl;
                cout << "testing:\n";
                cout << "\t largest = " << root->max_value << endl;
                cout << "\t smallest = " << root->min_value << endl;
                cout << "\n------------------------------------------------------------\n";
                cout << "final result: " << largest - smallest << endl;
            }

            clean_up(root);
        }

        cout << "DONE............\n";
    }

    void test_tree_as_array() {
          // arrange
        int test_cases;
        int number_of_operations = 100;
        int n;
        test_cases = 1000;
        for (int i = 0; i < test_cases; ++i) {
            n = MAX_RANGE;
            memset(min_tree, NIL, sizeof(min_tree));
            memset(max_tree, NIL, sizeof(max_tree));
            memset(added_to_interval, 0, sizeof(added_to_interval));
            memset(data, 0, sizeof(data));

            int operation;
            int x;
            int y;
            int added_amount;
            int smallest = 1;
            int largest = n;


            // initialize data for brute-force
            for (int i = 0; i < n; ++i) {
                data[i] = i + 1;
            }

            // build tree using array
            build_tree_as_array(1, 0, n - 1);
            for (int i = 0; i < number_of_operations; ++i) {
                operation = rand() % 2; 
                x = 1 + rand() % n;
                y = x + (rand() % (n - x + 1));
                added_amount = 1 + rand() % n;

                if (operation == 1) {
                    added_amount *= 1;
                }
                else {
                    added_amount *= -1;    
                }

                update_bruteforce(x - 1, y - 1, added_amount, smallest, largest, data, n);
                update_tree_as_array(1, 0, n - 1, x - 1, y - 1, added_amount);
                //assert(max_tree[1] == largest);
                //assert(min_tree[1] == smallest);

                cout << endl << endl;
                cout << "For n = " << n << endl;
                // show_data(data, n);
                cout << endl;
                cout << " and query is " << x - 1 << ", " << y - 1 << ", " << added_amount << endl;
                cout << "correct:\n";
                cout << "\t largest = " << largest << endl;
                cout << "\t smallest = " << smallest << endl;
                cout << "testing:\n";
                cout << "\t largest = " << max_tree[1] << endl;
                cout << "\t smallest = " << min_tree[1] << endl;
                cout << "\n------------------------------------------------------------\n";
                cout << "final result: " << largest - smallest << endl;
                cin.get();
            }
        }

        cout << "DONE............\n";
    }
}

int main() {
    // unit_test::test_against_brute_force();
    // unit_test::test_automation();    
    // unit_test::test_tree_as_array();
    inout_range_of_data();

    return 0;
}

答案 3 :(得分:0)

使段树变得懒惰似乎没有任何优势。最后,您需要查看每个单位坡度段的末端以获得最小值和最大值。所以你不妨热切地扩展它们。

相反,只需修改标准段树定义即可。树中的间隔将各自存储一个额外的整数d,因此我们将编写[d; lo,hi]。该树具有以下操作:

init(T, hi) // make a segment tree for the interval [0; 1,hi]
split(T, x, d)  // given there exists some interval [e; lo,hi],
                // in T where lo < x <= hi, replace this interval
                // with 2 new ones [e; lo,x-1] and [d; x,hi];
                // if x==lo, then replace with [e+d; lo,hi]

在初始化之后,我们使用两个拆分操作处理d到子区间[lo,hi]的添加:

split(T, lo, d); split(T, hi+1, -d);

我们的想法是,我们将d添加到位置lo和右侧的所有内容中,然后再将hi+1和右侧的内容再次减去。

构造树之后,在树叶上单个从左到右的通道让我们找到整数单位斜率段末端的值。这就是我们计算最小值和最大值所需的全部内容。更正式的是,如果树的叶间隔为[d_i; lo_i,hi_i]i=1..n按从左到右的顺序排列,那么我们要计算运行差异D_i = sum{i=1..n} d_i,然后计算L_i = lo_i + D_i和{{ 1}}。在示例中,我们从H_i = hi_i + D_i开始,然后在d = -4时分为4,在d = + 4时分为7以获得[0; 1,10]。然后是[0; 1,2] [-4; 3,6] [4; 7,10]L = [1,-1,7]。所以min是-1而max是10.这是一个简单的例子,但它一般都可以工作。

运行时间将是O(min(k log N,k ^ 2))其中N是最大初始范围值(在该示例中为10)并且k是应用的操作的数量。如果你在分裂的顺序上运气很差,就会出现k ^ 2的情况。如果随机化操作列表,预期时间将为O(k min(log N,log k))。

如果您有兴趣,我可以为您编写代码。但如果没有兴趣我就不会。

答案 4 :(得分:0)

这是链接。它具有延迟传播的分段树的实现和解释。尽管代码是用Java编写的,但它并不重要,因为只有两个函数'update'和'query',它们都是基于数组的。因此,这些函数也可以在C和C ++中使用,也无需任何修改。

http://isharemylearning.blogspot.in/2012/08/lazy-propagation-in-segment-tree.html