【Leetcode】307. Range Sum Query - Mutable

题目地址:

https://leetcode.com/problems/range-sum-query-mutable/

给定一个数组,实现若干操作,每次操作是查询区间和,或者修改数组中某个元素的值。

法1:树状数组。原理可以参考https://blog.csdn.net/qq_46105170/article/details/103870987。代码如下:

class NumArray {
 public:
#define lowbit(x) (x & -x)
  int n;
  vector<int> tr, A;

  void add(int k, int x) {
    for (; k <= n; k += lowbit(k)) tr[k] += x;
  }

  int sum(int k) {
    int res = 0;
    for (; k; k -= lowbit(k)) res += tr[k];
    return res;
  }

  NumArray(vector<int>& nums) {
    A.swap(nums);
    n = A.size();
    tr.resize(n + 1);
    for (int i = 1; i <= n; i++) {
      tr[i] += A[i - 1];
      int j = i + lowbit(i);
      if (j <= n) tr[j] += tr[i];
    }
  }

  void update(int i, int val) {
    add(i + 1, val - A[i]);
    A[i] = val;
  }

  int sumRange(int l, int r) { return sum(r + 1) - sum(l); }
};

初始化时间复杂度 O ( n ) O(n) O(n),其余操作时间复杂度 O ( log ⁡ n ) O(\log n) O(logn),空间 O ( n ) O(n) O(n)

法2:线段树。其主要思想是,每个树的节点存一个区间的信息,在查询的时候只需要区间长度每次分块查询然后汇总即可。更新的时候,先递归到叶子节点更新叶子,然后一路递归上去更新沿途的节点。代码实现方面,由于线段树是个接近于满二叉树的树,所以可以用数组实现。具体请看代码,代码如下:

class NumArray {
 public:
  struct Node {
    int l, r, sum;
  };
  vector<Node> tr;
  vector<int> A;

  void pushup(int u) { tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum; }

  void build(int u, int l, int r) {
    tr[u] = {l, r};
    if (l == r) {
      tr[u].sum = A[l - 1];
      return;
    }
    int mid = l + (r - l >> 1);
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    pushup(u);
  }

  void modify(int u, int i, int x) {
    if (tr[u].l == tr[u].r) {
      tr[u].sum = x;
      return;
    }

    int mid = tr[u].l + (tr[u].r - tr[u].l >> 1);
    if (i <= mid) modify(u << 1, i, x);
    if (i > mid) modify(u << 1 | 1, i, x);
    pushup(u);
  }

  int query(int u, int l, int r) {
    if (l <= tr[u].l && tr[u].r <= r) return tr[u].sum;
    int res = 0;
    int mid = tr[u].l + (tr[u].r - tr[u].l >> 1);
    if (l <= mid) res = query(u << 1, l, r);
    if (r > mid) res += query(u << 1 | 1, l, r);
    return res;
  }

  NumArray(vector<int>& nums) {
    A.swap(nums);
    tr.resize(A.size() << 2);
    build(1, 1, A.size());
  }

  void update(int index, int val) { modify(1, index + 1, val); }

  int sumRange(int left, int right) { return query(1, left + 1, right + 1); }
};

所有操作时间空间复杂度都与树状数组一样。

一般而言,同样的题目,如果线段树和树状数组都可以做的话,树状数组的效率更高。但是树状数组适用的情景不如线段树多,所以两个数据结构各有优劣。本题两者都可以做。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值