树状数组(Fenwick Tree)详解:从原理到实战
📅 2026/7/25 19:15:20
👁️ 次浏览
1. 什么是树状数组树状数组Fenwick Tree是一种用于高效处理前缀和查询与单点更新的数据结构。它能在 O(log n) 时间内完成这两种操作空间复杂度为 O(n)比朴素的前缀和数组更新 O(n)查询 O(1)和线段树功能更强但代码更复杂在特定场景下更加简洁高效。树状数组由 Peter Fenwick 于 1994 年提出主要用于解决以下问题频繁修改数组中的某个元素频繁查询数组某个前缀区间的和2. 核心原理2.1 二进制低位技术Lowbit树状数组的核心是一个巧妙的二进制操作lowbit(x) x -x。这个操作可以取出 x 二进制表示中最低位的 1 及其后面的 0。例如lowbit(6) lowbit(110₂) 10₂ 2lowbit(12) lowbit(1100₂) 100₂ 4这个操作决定了树状数组中每个元素管理的区间范围。2.2 树状数组的结构假设原数组为arr[1..n]通常下标从 1 开始树状数组tree[1..n]的每个元素tree[i]管理原数组的一段区间tree[i]管理原数组从i - lowbit(i) 1到i的元素和这种设计使得更新操作修改arr[i]时需要更新所有包含 i 的tree[j]通过j i lowbit(i)不断向上跳转查询操作查询前缀和sum[1..i]时通过i i - lowbit(i)不断向前累加3. 基本操作实现3.1 单点更新// 在位置 i 增加 delta void update(int i, int delta) { while (i n) { tree[i] delta; i lowbit(i); } }3.2 前缀和查询// 查询前缀和 sum[1..i] int query(int i) { int sum 0; while (i 0) { sum tree[i]; i - lowbit(i); } return sum; }3.3 区间和查询// 查询区间和 sum[l..r] int rangeQuery(int l, int r) { return query(r) - query(l - 1); }4. 初始化构建树状数组有多种初始化方式逐个插入对每个元素调用 update时间复杂度 O(n log n)前缀和预处理利用 tree[i] sum[i] - sum[i - lowbit(i)]时间复杂度 O(n)// 高效初始化 void init(int[] arr) { n arr.length; tree new int[n 1]; int[] prefix new int[n 1]; for (int i 1; i n; i) { prefix[i] prefix[i - 1] arr[i - 1]; tree[i] prefix[i] - prefix[i - lowbit(i)]; } }5. 实战应用场景5.1 逆序对问题树状数组可以高效统计逆序对数量int countInversions(int[] nums) { // 离散化处理 int[] sorted nums.clone(); Arrays.sort(sorted); MapInteger, Integer rank new HashMap(); for (int i 0; i sorted.length; i) { rank.put(sorted[i], i 1); } int count 0; FenwickTree ft new FenwickTree(nums.length); for (int i nums.length - 1; i 0; i--) { int r rank.get(nums[i]); count ft.query(r - 1); // 统计比当前数小的数量 ft.update(r, 1); } return count; }5.2 区间更新 单点查询通过差分数组技巧树状数组可以支持区间更新// 区间 [l, r] 每个元素增加 delta void rangeUpdate(int l, int r, int delta) { update(l, delta); update(r 1, -delta); } // 单点查询 int pointQuery(int i) { return query(i); }5.3 二维树状数组扩展到二维平面支持矩阵的子矩阵求和与单点更新class FenwickTree2D { private int[][] tree; private int rows, cols; public FenwickTree2D(int m, int n) { rows m; cols n; tree new int[m 1][n 1]; } void update(int x, int y, int delta) { for (int i x; i rows; i lowbit(i)) { for (int j y; j cols; j lowbit(j)) { tree[i][j] delta; } } } int query(int x, int y) { int sum 0; for (int i x; i 0; i - lowbit(i)) { for (int j y; j 0; j - lowbit(j)) { sum tree[i][j]; } } return sum; } }6. 与线段树的对比特性树状数组线段树代码复杂度简单约 20 行较复杂约 50-100 行时间复杂度O(log n) 更新/查询O(log n) 更新/查询空间复杂度O(n)O(4n)功能范围前缀和、逆序对、差分任意区间操作最大/最小值、区间修改等适用场景前缀和频繁操作复杂区间查询与更新7. 常见问题与优化7.1 下标从 1 开始树状数组通常下标从 1 开始因为 lowbit(0) 0 会导致死循环。实际使用时需要注意下标转换。7.2 离散化技巧当数值范围很大但数据量不大时可以通过离散化将原始值映射到紧凑的整数区间减少树状数组大小。7.3 负数和浮点数处理树状数组本身支持负数和浮点数但需要注意更新时 delta 可以是负数查询结果可能溢出需要使用更大的数据类型如 long8. 总结树状数组是一种优雅高效的数据结构特别适合处理前缀和相关的动态统计问题。它的核心优势在于代码简洁核心操作仅需几行代码效率高O(log n) 的时间复杂度满足大多数竞赛和工程需求扩展性强可以扩展到二维、支持差分技巧等掌握树状数组的关键在于理解 lowbit 操作的原理以及如何通过二进制分解将前缀和分解为若干子区间的和。在实际应用中树状数组经常用于统计问题、逆序对计算、区间更新等场景是算法竞赛和面试中的常考知识点。
先上一段 AI 给我生成的代码,你就能懂我为什么说它"红了一整页":
// ❌ DevEco Code 的 AI 自动补全出来的 CaseList 组件开头
Component
export struct CaseList {State cases: CaseItem[] CaseService.getHotList() // 编译报错State key…
📅 2026/7/25 19:14:20
146、AI AWB方法:基于卷积神经网络的色温估计与场景分类调优 一、从一次翻车现场说起
去年做某旗舰机项目,实验室里AWB调得漂漂亮亮,DNP灯箱、X-Rite色卡、Macbeth checker全过。结果客户在东京街头拍了一组夜景——霓虹灯、LED广告牌、路灯混在一起,画面直接翻车:白色墙…
📅 2026/7/25 19:14:20
147、OIS光学防抖系统设计:陀螺仪标定、音圈马达驱动与滚珠式vs悬丝式对比
去年在调试某旗舰机型的OIS时,遇到一个诡异现象:手机放在桌上静止,预览画面却在低频抖动,像有人在偷偷晃手机。抓了陀螺仪原始数据一看,零偏漂移居然达到0.5/s,这要是放在车载场景,画面早晃成…
📅 2026/7/25 19:14:20
一、项目背景随着城市轨道交通、公路隧道、综合管廊基建工程快速推进,盾构机已成为地下隧道施工的核心主力设备。盾构机在隧道内持续长距离掘进,施工环境密闭、潮湿、高粉尘,设备运行姿态控制、推进压力、注浆流量、刀盘参数等核心数据&#…
📅 2026/7/25 20:52:01
最近在AI生成模型领域,一个名为Un-0的新模型引起了不小的震动。它号称是“第一个用物理做计算原语的大规模生成模型”,其背后“模拟耦合振子系统”的物理计算底座,更是提出了将AI能耗降低1000倍的惊人潜力。对于开发者而言,这不仅是学术上的新突破,更可能预示着未来AI模型…
📅 2026/7/25 20:52:01
如果你正在寻找一个能让你快速构建、部署和管理 AI 应用,尤其是那些需要复杂逻辑和流程的 Agentic AI 应用,却苦于从零开发的高门槛和长周期,那么 Dify 的出现,可能正是你等待的答案。它不是一个简单的 API 包装器,而是一个宣称能提供“生产级 Agentic 工作流”的一站式平…
📅 2026/7/25 20:52:01
1. 项目概述:为何要深究SoC的系统控制与互联?在嵌入式系统开发,尤其是涉及像德州仪器66AK2L06这类高性能异构多核SoC时,很多工程师的注意力往往集中在应用层的算法实现、任务调度或者具体外设驱动上。然而,我多年的踩坑…
📅 2026/7/25 20:52:01
1. 项目概述:为什么程序集是Unity开发的“隐形战场”刚接触Unity开发的朋友,可能都经历过这样的场景:项目跑得好好的,突然某个脚本死活不生效,或者改了一行代码,Unity编辑器却像没看见一样,非得…
📅 2026/7/25 20:52:01
咱们做实体生意或者本地服务的,最近是不是总听人念叨“geo 搜索”这个词?好像不弄这个,客户就找不到你似的。我前阵子跟几个开餐饮和汽修的朋友聊,发现大家有个通病:把百度地图或者高德地图当成了唯一的救命稻草。其实啊,这思路有点窄了。真正的本地流量,藏在那些你看不…
📅 2026/7/25 20:50:46
1. 项目概述:为什么我们要“手撕”string类?在C的学习道路上,尤其是从C语言过渡到C的“初阶”阶段,string类绝对是一个绕不开的核心。标准库里的std::string用起来太方便了,、find、substr,几个操作符和函数…
📅 2026/7/25 0:00:17
1. 先搞清楚“三角洲寻宝鼠”到底是什么工具从名称来看,“三角洲寻宝鼠”更像是一个资源查找或文件检索类工具,而不是游戏或娱乐软件。这类工具的核心价值在于帮助用户快速定位特定资源,比如文档、图片、压缩包或特定格式的文件。如果你经常需…
📅 2026/7/25 0:00:17
一、直达船员,预警链路最短营运船舶强制标配 VHF 船载电台,属于驾驶室常态化值守设备;预警语音直接传递至驾驶人员,区别于岸上声光报警(船员经常听不到)、短信 / 小程序(船员极少主动查看&#…
📅 2026/7/25 0:00:17
1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…
📅 2026/7/25 1:09:03
1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…
📅 2026/7/25 1:09:03
更多请点击:
https://intelliparadigm.com
第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…
📅 2026/7/25 1:09:03
目录
第一步:选对模板,省心一半
第二步:打开扫码点餐功能
开启功能按钮
桌台管理与桌码生成
第三步:个性化设计,打造品牌感
调整点餐页面
设置点餐规则 你还在让顾客站着排队点餐吗?2025年ÿ…
📅 2026/7/25 7:09:18
在业务中快速构建一个能理解私有文档、准确回答专业问题的智能助手,是很多开发团队面临的共同挑战。传统方案往往需要从零开始搭建复杂的 RAG(检索增强生成)系统,涉及文档解析、向量化、检索、大模型调用等多个环节,整…
📅 2026/7/25 17:09:47
FAE放射组学分析工具:医学影像特征探索的完整解决方案 【免费下载链接】FAE FeAture Explorer 项目地址: https://gitcode.com/gh_mirrors/fae/FAE
你是否曾经面对海量医学影像数据感到无从下手?想要从CT、MRI等影像中提取有价值的定量特征&#…
📅 2026/7/25 5:09:14