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

📅  最后修改于: 2023-12-03 15:40:03.148000             🧑  作者: Mango

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

介绍

本文将介绍使用堆栈的段树来实现数组中的范围总和和更新操作。堆栈是一种数据结构,它道具有LIFO(后进先出)的特性。段树是一种树形结构用于处理区间操作。通过将堆栈和段树结合,我们可以在常数时间内进行范围总和和更新操作。

堆栈的实现

堆栈可以用数组或链表来实现。这里我们使用链表来实现堆栈。以下是堆栈类的实现:

class StackNode {
  int val;
  StackNode next;
  public StackNode(int val) {
    this.val = val;
  }
}

class Stack {
  StackNode head;

  public Stack() {
    head = null;
  }

  public void push(int x) {
    StackNode node = new StackNode(x);
    node.next = head;
    head = node;
  }

  public int pop() {
    if (isEmpty()) {
      throw new RuntimeException("stack is empty");
    }
    int result = head.val;
    head = head.next;
    return result;
  }

  public int peek() {
    if (isEmpty()) {
      throw new RuntimeException("stack is empty");
    }
    return head.val;
  }

  public boolean isEmpty() {
    return head == null;
  }
}
段树的实现

段树是一种树形结构,它将数组分成多个不重叠的区间。叶子节点存储原始数组的值,非叶子节点存储区间的总和。以下是段树类的实现:

class SegmentTreeNode {
  int start, end;
  int sum;
  SegmentTreeNode left, right;
  public SegmentTreeNode(int start, int end) {
    this.start = start;
    this.end = end;
    left = null;
    right = null;
  }
}

class SegmentTree {
  private SegmentTreeNode root;

  public SegmentTree(int[] nums) {
    root = build(nums, 0, nums.length - 1);
  }

  public void update(int i, int val) {
    update(root, i, val);
  }

  public int sumRange(int i, int j) {
    return sumRange(root, i, j);
  }

  private SegmentTreeNode build(int[] nums, int start, int end) {
    if (start > end) {
      return null;
    }
    SegmentTreeNode node = new SegmentTreeNode(start, end);
    if (start == end) {
      node.sum = nums[start];
    } else {
      int mid = (start + end) / 2;
      node.left = build(nums, start, mid);
      node.right = build(nums, mid + 1, end);
      node.sum = node.left.sum + node.right.sum;
    }
    return node;
  }

  private void update(SegmentTreeNode node, int i, int val) {
    if (node.start == i && node.end == i) {
      node.sum = val;
    } else {
      int mid = (node.start + node.end) / 2;
      if (i <= mid) {
        update(node.left, i, val);
      } else {
        update(node.right, i, val);
      }
      node.sum = node.left.sum + node.right.sum;
    }
  }

  private int sumRange(SegmentTreeNode node, int i, int j) {
    if (node.start == i && node.end == j) {
      return node.sum;
    } else {
      int mid = (node.start + node.end) / 2;
      if (j <= mid) {
        return sumRange(node.left, i, j);
      } else if (i >= mid + 1) {
        return sumRange(node.right, i, j);
      } else {
        return sumRange(node.left, i, mid) + sumRange(node.right, mid + 1, j);
      }
    }
  }
}
堆栈和段树的结合

现在我们将堆栈和段树结合起来。我们使用一个堆栈来存储更新操作的信息。每个更新操作由三个参数组成:起始索引、结束索引和新值。我们可以在堆栈中存储一个三元组,每次更新时将这个三元组推入堆栈中。每次查询总和时,我们将堆栈中所有未处理的更新操作应用于段树,然后查询总和。

以下是使用堆栈的段树的实现:

class StackSegmentTree {
  private SegmentTree tree;
  private Stack stack;

  public StackSegmentTree(int[] nums) {
    tree = new SegmentTree(nums);
    stack = new Stack();
  }

  public void update(int i, int j, int val) {
    stack.push(new int[] {i, j, val});
  }

  public int sumRange(int i, int j) {
    while (!stack.isEmpty()) {
      int[] update = stack.pop();
      tree.update(update[0], update[1], update[2]);
    }
    return tree.sumRange(i, j);
  }
}
总结

本文介绍了如何使用堆栈的段树来实现数组中的范围总和和更新操作。堆栈和段树的结合可以在常数时间内进行范围总和和更新操作。这种方法的时间复杂度为O(logN),其中N为数组的长度。