📜  数组中的范围总和和更新:使用堆栈的段树

📅  最后修改于: 2021-09-06 05:42:38             🧑  作者: Mango

给定一个由N 个整数组成的数组arr[] 。任务是执行以下操作:

  1. 将值X添加到从索引AB 的所有元素,其中0 ≤ A ≤ B ≤ N-1
  2. 求从索引LR的元素的总和,其中0 ≤ L ≤ R ≤ N-1在给上述数组的更新之前和之后。

例子:

方法:
本文讨论了针对给定问题使用 Segment Tree 的递归方法。在这篇文章中,我们将讨论一种使用堆栈数据结构来避免递归的方法。
以下是使用 Stack 实现 Segment Tree 的步骤:

  1. 这个想法是使用元组来存储堆栈中具有节点编号和范围索引的状态。
  2. 对于构建段树
    • 将根节点作为元组推入堆栈:
Stack S;
start = 0, end = arr_size - 1
S.emplace(1, start, end)
  • 从堆栈中弹出元素并执行以下操作直到堆栈变空:
    1. 如果起始索引等于结束索引,那么我们到达叶节点并将 Segment 树数组中的值更新为给定数组中当前索引处的值。
    2. 否则插入带有当前节点的标志元组作为S.emplace(current_node, INF, INF)以反转评估顺序并插入具有左右孩子值的元组:
  1. 如果开始索引和结束索引与INF相同,则将当前索引处的 Segment Tree 值更新为:
  1. 对于更新树
    • 将根节点推送到堆栈,就像构建 Segment Tree 一样。
    • 从堆栈中弹出元素并执行以下操作直到堆栈变空:
      1. 如果当前节点有任何挂起的更新,则首先更新到当前节点。
      2. 如果当前节点范围完全在更新查询范围内,则使用该值更新当前节点。
      3. 如果当前节点范围与更新查询范围重叠,则按照上述方法将左孩子和右孩子的元组推送到堆栈中。
      4. 使用上面左右孩子的结果更新查询。
  2. 对于更新查询
    • 将根节点推送到堆栈,就像构建 Segment Tree 一样。
    • 从堆栈中弹出元素并执行以下操作直到堆栈变空:
      1. 如果当前节点范围位于给定查询之外,则继续下一次迭代。
      2. 如果当前节点范围完全在更新查询范围内,则用当前节点值更新结果。
      3. 如果当前节点范围与更新查询范围重叠,则按照上述方法将左孩子和右孩子的元组推送到堆栈中。
      4. 使用从左右子节点获得的值更新结果。

下面是上述方法的实现:

CPP
#include"bits/stdc++.h"
using namespace std;
 
constexpr static int MAXSIZE = 1000;
constexpr static int INF
    = numeric_limits::max();
 
// Segment Tree array
int64_t tree[MAXSIZE];
 
// Lazy Update array
int64_t lazy[MAXSIZE];
 
// This tuple will hold tree state
// the stacks
using QueryAdaptor
    = tuple;
 
// Build our segment tree
void build_tree(int64_t* arr,
                int64_t arr_size)
{
   
    // Stack will use to update
    // the tree value
    stack st;
 
    // Emplace the root of the tree
    st.emplace(1, 0, arr_size - 1);
 
    // Repeat until empty
    while (!st.empty()) {
 
        // Take the indexes at the
        // top of the stack
        int64_t currnode, curra, currb;
 
        // value at the top of the
        // stack
        tie(currnode, curra, currb) = st.top();
 
        // Pop the value from the
        // stack
        st.pop();
 
        // Flag with INF ranges are merged
        if (curra == INF && currb == INF) {
            tree[currnode] = tree[currnode * 2]
                             + tree[currnode * 2 + 1];
        }
 
        // Leaf node
        else if (curra == currb) {
            tree[currnode] = arr[curra];
        }
 
        else {
 
            // Insert flag node inverse
            // order of evaluation
            st.emplace(currnode, INF, INF);
            int64_t mid = (curra + currb) / 2;
 
            // Push children
            st.emplace(currnode * 2,
                       curra, mid);
            st.emplace(currnode * 2 + 1,
                       mid + 1, currb);
        }
    }
}
 
// A utility function that propagates
// updates lazily down the tree
inline void push_down(int64_t node,
                      int64_t a,
                      int64_t b)
{
    if (lazy[node] != 0) {
        tree[node] += lazy[node] * (b - a + 1);
 
        if (a != b) {
            lazy[2 * node] += lazy[node];
            lazy[2 * node + 1] += lazy[node];
        }
 
        lazy[node] = 0;
    }
}
 
// Iterative Range_Update function to
// add val to all elements in the
// range i-j (inclusive)
void update_tree(int64_t arr_size,
                 int64_t i,
                 int64_t j,
                 int64_t val)
{
 
    // Initialize the stack
    stack st;
 
    // Emplace the root of the tree
    st.emplace(1, 0, arr_size - 1);
 
    // Work until empty
    while (!st.empty()) {
 
        // Take the indexes at the
        // top of the stack
        int64_t currnode, curra, currb;
        tie(currnode, curra, currb) = st.top();
        st.pop();
 
        // Flag with INF ranges are merged
        if (curra == INF && currb == INF) {
            tree[currnode] = tree[currnode * 2]
                             + tree[currnode * 2 + 1];
        }
 
        // Traverse the previous updates
        // down the tree
        else {
            push_down(currnode, curra, currb);
 
            // No overlap condition
            if (curra > currb || curra > j
                || currb < i) {
                continue;
            }
 
            // Total overlap condition
            else if (curra >= i && currb <= j)
            {
               
                // Update lazy array
                tree[currnode] += val * (currb - curra + 1);
 
                if (curra != currb) {
                    lazy[currnode * 2] += val;
                    lazy[currnode * 2 + 1] += val;
                }
            }
 
            // Partial Overlap
            else
            {
               
                // Insert flag node inverse
                // order of evaluation
                st.emplace(currnode, INF, INF);
 
                int64_t mid = (curra + currb) / 2;
 
                // Push children
                st.emplace(currnode * 2,
                           curra, mid);
                st.emplace(currnode * 2 + 1,
                           mid + 1, currb);
            }
        }
    }
}
 
// A function that find the sum of
// all elements in the range i-j
int64_t query(int64_t arr_size,
              int64_t i,
              int64_t j)
{
   
    // Initialize stack
    stack st;
 
    // Emplace root of the tree
    st.emplace(1, 0, arr_size - 1);
 
    int64_t result = 0;
 
    while (!st.empty())
    {
 
        // Take the indexes at the
        // top of the stack
        int64_t currnode, curra, currb;
        tie(currnode, curra, currb) = st.top();
        st.pop();
 
        // Traverse the previous updates
        // down the tree
        push_down(currnode, curra, currb);
 
        // No overlap
        if (curra > currb || curra > j
            || currb < i) {
            continue;
        }
 
        // Total Overlap
        else if (curra >= i && currb <= j) {
            result += tree[currnode];
        }
 
        // Partial Overlap
        else {
            std::int64_t mid = (curra + currb) / 2;
 
            // Push children
            st.emplace(currnode * 2,
                       curra, mid);
            st.emplace(currnode * 2 + 1,
                       mid + 1, currb);
        }
    }
 
    return result;
}
 
// Driver Code
int main()
{
   
    // Initialize our trees with 0
    memset(tree, 0, sizeof(int64_t) * MAXSIZE);
    memset(lazy, 0, sizeof(int64_t) * MAXSIZE);
 
    int64_t arr[] = { 1, 3, 5, 7, 9, 11 };
    int n = sizeof(arr) / sizeof(arr[0]);
 
    // Build segment tree from given array
    build_tree(arr, n);
 
    // Print sum of values in array
    // from index 1 to 3
    cout << "Sum of values in given range = "
         << query(n, 1, 3)
         << endl;
 
    // Add 10 to all nodes at indexes
    // from 1 to 5
    update_tree(n, 1, 5, 10);
 
    // Find sum after the value is updated
    cout << "Updated sum of values in given range = "
         << query(n, 1, 3)
         << endl;
 
    return 0;
}


Java
// Java implementation of the approach
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
class GFG
{
 
    static final int MAXSIZE = 1000;
    static final int INF = (int) Double.POSITIVE_INFINITY;
 
    // Segment Tree array
    static int[] tree = new int[MAXSIZE];
 
    // Lazy Update array
    static int[] lazy = new int[MAXSIZE];
 
    // Build our segment tree
    static void build_tree(int[] arr, int arr_size)
    {
       
        // Stack will use to update
        // the tree value
        Stack> st = new Stack<>();
 
        // push the root of the tree
        st.push(Arrays.asList(1, 0, arr_size - 1));
 
        // Repeat until empty
        while (!st.isEmpty())
        {
 
            // Take the indexes at the
            // top of the stack
            int currnode, curra, currb;
 
            // value at the top of the
            // stack
            List temp = st.peek();
            currnode = temp.get(0);
            curra = temp.get(1);
            currb = temp.get(2);
 
            // Pop the value from the
            // stack
            st.pop();
 
            // Flag with INF ranges are merged
            if (curra == INF && currb == INF)
            {
                tree[currnode] = tree[currnode * 2] +
                  tree[currnode * 2 + 1];
            }
 
            // Leaf node
            else if (curra == currb)
            {
                tree[currnode] = arr[curra];
            }
 
            else {
 
                // Insert flag node inverse
                // order of evaluation
                st.push(Arrays.asList(currnode, INF, INF));
 
                int mid = (curra + currb) / 2;
 
                // Push children
                st.push(Arrays.asList(currnode * 2, curra, mid));
                st.push(Arrays.asList(currnode * 2 + 1, mid + 1, currb));
            }
        }
    }
 
    // A utility function that propagates
    // updates lazily down the tree
    static void push_down(int node, int a, int b)
    {
        if (lazy[node] != 0)
        {
            tree[node] += lazy[node] * (b - a + 1);
 
            if (a != b)
            {
                lazy[2 * node] += lazy[node];
                lazy[2 * node + 1] += lazy[node];
            }
 
            lazy[node] = 0;
        }
    }
 
    // Iterative Range_Update function to
    // add val to all elements in the
    // range i-j (inclusive)
    static void update_tree(int arr_size, int i,
                            int j, int val)
    {
 
        // Initialize the stack
        Stack> st = new Stack<>();
 
        // push the root of the tree
        st.push(Arrays.asList(1, 0, arr_size - 1));
 
        // Work until empty
        while (!st.isEmpty())
        {
 
            // Take the indexes at the
            // top of the stack
            int currnode, curra, currb;
            List temp = st.peek();
            currnode = temp.get(0);
            curra = temp.get(1);
            currb = temp.get(2);
            st.pop();
 
            // Flag with INF ranges are merged
            if (curra == INF && currb == INF)
            {
                tree[currnode] = tree[currnode * 2] +
                  tree[currnode * 2 + 1];
            }
 
            // Traverse the previous updates
            // down the tree
            else
            {
                push_down(currnode, curra, currb);
 
                // No overlap condition
                if (curra > currb || curra > j || currb < i)
                {
                    continue;
                }
 
                // Total overlap condition
                else if (curra >= i && currb <= j)
                {
                   
                    // Update lazy array
                    tree[currnode] += val * (currb - curra + 1);
 
                    if (curra != currb)
                    {
                        lazy[currnode * 2] += val;
                        lazy[currnode * 2 + 1] += val;
                    }
                }
 
                // Partial Overlap
                else
                {
                   
                    // Insert flag node inverse
                    // order of evaluation
                    st.push(Arrays.asList(currnode, INF, INF));
 
                    int mid = (curra + currb) / 2;
 
                    // Push children
                    st.push(Arrays.asList(currnode * 2, curra, mid));
                    st.push(Arrays.asList(currnode * 2 + 1,
                                          mid + 1, currb));
                }
            }
        }
    }
 
    // A function that find the sum of
    // all elements in the range i-j
    static int query(int arr_size, int i, int j)
    {
       
        // Initialize stack
        Stack> st = new Stack<>();
 
        // push root of the tree
        st.push(Arrays.asList(1, 0, arr_size - 1));
 
        int result = 0;
 
        while (!st.isEmpty())
        {
 
            // Take the indexes at the
            // top of the stack
            int currnode, curra, currb;
            List temp = st.peek();
            currnode = temp.get(0);
            curra = temp.get(1);
            currb = temp.get(2);
            st.pop();
 
            // Traverse the previous updates
            // down the tree
            push_down(currnode, curra, currb);
 
            // No overlap
            if (curra > currb || curra > j || currb < i)
            {
                continue;
            }
 
            // Total Overlap
            else if (curra >= i && currb <= j)
            {
                result += tree[currnode];
            }
 
            // Partial Overlap
            else
            {
                int mid = (curra + currb) / 2;
 
                // Push children
                st.push(Arrays.asList(currnode * 2, curra, mid));
                st.push(Arrays.asList(currnode * 2 + 1, mid + 1, currb));
            }
        }
 
        return result;
    }
 
    // Driver Code
    public static void main(String[] args)
    {
 
        // Initialize our trees with 0
        Arrays.fill(tree, 0);
        Arrays.fill(lazy, 0);
 
        int arr[] = { 1, 3, 5, 7, 9, 11 };
        int n = arr.length;
 
        // Build segment tree from given array
        build_tree(arr, n);
 
        // Print sum of values in array
        // from index 1 to 3
        System.out.printf("Sum of values in given range = %d\n", query(n, 1, 3));
 
        // Add 10 to all nodes at indexes
        // from 1 to 5
        update_tree(n, 1, 5, 10);
 
        // Find sum after the value is updated
        System.out.printf("Updated sum of values in given range = %d\n", query(n, 1, 3));
    }
}
 
// This code is contributed by sanjeev2552


输出:
Sum of values in given range = 15
Updated sum of values in given range = 45

时间复杂度:

  • 对于树构造 O(N),树中有 (2n-1) 个节点,每个节点的值计算一次。
  • 对于查询 O(log N),为了查询一个总和,我们在每个级别最多处理四个节点,级别数为 log N。
  • 对于更新 O(log N),使用惰性传播更新树是 O(Log N),因为我们更新树的根,然后只更新树的范围在每个级别重叠的部分。

如果您想与行业专家一起参加直播课程,请参阅Geeks Classes Live