C语言树状数组与线段树实例分析

发布时间:2022-04-12 17:34:27 作者:zzz
来源:亿速云 阅读:131

C语言树状数组与线段树实例分析

目录

  1. 引言
  2. 树状数组
  3. 线段树
  4. 树状数组与线段树的比较
  5. 总结

引言

在计算机科学中,树状数组(Fenwick Tree)和线段树(Segment Tree)是两种常用的数据结构,用于高效地处理区间查询和更新操作。它们在解决各种算法问题时表现出色,尤其是在需要频繁进行区间求和、区间最值查询等操作的场景中。本文将详细介绍树状数组和线段树的基本概念、实现原理、代码实现以及应用实例,并通过对比分析它们的优缺点,帮助读者更好地理解和应用这两种数据结构。

树状数组

基本概念

树状数组,也称为二叉索引树(Binary Indexed Tree, BIT),是一种用于高效计算前缀和的数据结构。它能够在O(log n)的时间复杂度内完成单点更新和区间查询操作。树状数组的主要优点是实现简单、代码量少,且在实际应用中性能优异。

实现原理

树状数组的核心思想是利用二进制的特性,通过维护一个数组来存储部分和,从而快速计算任意区间的前缀和。具体来说,树状数组的每个节点存储了从该节点开始向前的一段区间的和。通过巧妙地利用二进制的低位和高位,树状数组能够在O(log n)的时间内完成更新和查询操作。

代码实现

以下是树状数组的C语言实现代码:

#include <stdio.h>
#include <stdlib.h>

#define MAX_N 1000

int tree[MAX_N + 1];

// 更新操作:将位置i的值增加delta
void update(int i, int delta) {
    while (i <= MAX_N) {
        tree[i] += delta;
        i += i & -i;
    }
}

// 查询操作:返回位置1到i的前缀和
int query(int i) {
    int sum = 0;
    while (i > 0) {
        sum += tree[i];
        i -= i & -i;
    }
    return sum;
}

int main() {
    int n, m;
    scanf("%d %d", &n, &m);

    for (int i = 1; i <= n; i++) {
        int val;
        scanf("%d", &val);
        update(i, val);
    }

    for (int i = 0; i < m; i++) {
        int type, a, b;
        scanf("%d %d %d", &type, &a, &b);
        if (type == 1) {
            update(a, b);
        } else {
            printf("%d\n", query(b) - query(a - 1));
        }
    }

    return 0;
}

应用实例

假设我们有一个数组arr,我们需要频繁地进行以下两种操作:

  1. 更新操作:将arr[i]的值增加delta
  2. 查询操作:查询arr[1]arr[i]的前缀和。

使用树状数组,我们可以在O(log n)的时间内完成这两种操作。例如,给定一个数组arr = [1, 2, 3, 4, 5],我们可以通过以下步骤构建树状数组:

  1. 初始化树状数组tree为全0。
  2. 依次调用update(i, arr[i]),将数组元素插入树状数组。
  3. 查询arr[1]arr[3]的前缀和,调用query(3),返回6。

线段树

基本概念

线段树是一种用于处理区间查询和更新操作的数据结构。它能够在O(log n)的时间复杂度内完成区间查询和单点更新操作。线段树的主要优点是灵活性高,能够处理各种复杂的区间操作,如区间求和、区间最值查询等。

实现原理

线段树的核心思想是将区间划分为若干个小区间,并将这些小区间的信息存储在树结构中。每个节点代表一个区间,并存储该区间的相关信息(如区间和、区间最值等)。通过递归地划分区间,线段树能够在O(log n)的时间内完成区间查询和更新操作。

代码实现

以下是线段树的C语言实现代码:

#include <stdio.h>
#include <stdlib.h>

#define MAX_N 1000

int arr[MAX_N];
int tree[4 * MAX_N];

// 构建线段树
void build(int node, int start, int end) {
    if (start == end) {
        tree[node] = arr[start];
    } else {
        int mid = (start + end) / 2;
        build(2 * node, start, mid);
        build(2 * node + 1, mid + 1, end);
        tree[node] = tree[2 * node] + tree[2 * node + 1];
    }
}

// 更新操作:将位置idx的值增加val
void update(int node, int start, int end, int idx, int val) {
    if (start == end) {
        arr[idx] += val;
        tree[node] += val;
    } else {
        int mid = (start + end) / 2;
        if (start <= idx && idx <= mid) {
            update(2 * node, start, mid, idx, val);
        } else {
            update(2 * node + 1, mid + 1, end, idx, val);
        }
        tree[node] = tree[2 * node] + tree[2 * node + 1];
    }
}

// 查询操作:返回区间[l, r]的和
int query(int node, int start, int end, int l, int r) {
    if (r < start || end < l) {
        return 0;
    }
    if (l <= start && end <= r) {
        return tree[node];
    }
    int mid = (start + end) / 2;
    int p1 = query(2 * node, start, mid, l, r);
    int p2 = query(2 * node + 1, mid + 1, end, l, r);
    return p1 + p2;
}

int main() {
    int n, m;
    scanf("%d %d", &n, &m);

    for (int i = 1; i <= n; i++) {
        scanf("%d", &arr[i]);
    }

    build(1, 1, n);

    for (int i = 0; i < m; i++) {
        int type, a, b;
        scanf("%d %d %d", &type, &a, &b);
        if (type == 1) {
            update(1, 1, n, a, b);
        } else {
            printf("%d\n", query(1, 1, n, a, b));
        }
    }

    return 0;
}

应用实例

假设我们有一个数组arr,我们需要频繁地进行以下两种操作:

  1. 更新操作:将arr[i]的值增加delta
  2. 查询操作:查询arr[l]arr[r]的区间和。

使用线段树,我们可以在O(log n)的时间内完成这两种操作。例如,给定一个数组arr = [1, 2, 3, 4, 5],我们可以通过以下步骤构建线段树:

  1. 初始化数组arr
  2. 调用build(1, 1, n),构建线段树。
  3. 查询arr[1]arr[3]的区间和,调用query(1, 1, n, 1, 3),返回6。

树状数组与线段树的比较

时间复杂度

空间复杂度

实现复杂度

适用场景

总结

树状数组和线段树是两种常用的数据结构,用于高效地处理区间查询和更新操作。树状数组实现简单,适用于单点更新和前缀和查询的场景;而线段树灵活性高,适用于各种复杂的区间操作。在实际应用中,应根据具体需求选择合适的数据结构,以达到最佳的性能和效果。

通过本文的介绍和实例分析,相信读者对树状数组和线段树有了更深入的理解。希望本文能够帮助读者在实际编程中更好地应用这两种数据结构,解决各种算法问题。

推荐阅读:
  1. 线段树与可持久化
  2. C语言中怎么实现一个树状数组

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

c语言

上一篇:mysql中not in隐含陷阱是什么

下一篇:原生js怎么实现弹动小球效果

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》