在算法竞赛和实际工程开发中,我们经常会遇到需要频繁对数组进行区间查询(如求和、最大值、最小值)和区间更新的问题。如果每次都遍历整个区间,时间复杂度会很高(O(n))。这时,线段树(Segment Tree)就派上用场了!
本教程将带你从零开始,用 Java语言 实现一个基础但功能完整的线段树,即使是编程小白也能轻松理解。
线段树是一种二叉树结构,用于存储区间信息。它将一个大区间不断二分,直到每个叶子节点代表一个单元素区间。每个非叶子节点则代表其左右子节点区间的合并结果(如和、最大值等)。

例如,对于数组 [1, 3, 5, 7, 9, 11],线段树可以快速回答“区间 [1,4] 的和是多少?”或“将区间 [2,5] 所有元素加 2”这类问题,时间复杂度仅为 O(log n)。
线段树主要支持两种操作:
为了高效处理区间更新,我们通常使用一种叫懒标记(Lazy Propagation)的技术,避免每次更新都递归到底层。
下面是一个完整的 Java 线段树实现,包含构建、查询和带懒标记的区间更新功能。
public class SegmentTree { private long[] tree; // 线段树数组 private long[] lazy; // 懒标记数组 private int n; // 原数组长度 // 构造函数:根据原数组构建线段树 public SegmentTree(int[] arr) { this.n = arr.length; this.tree = new long[4 * n]; // 通常开4倍空间 this.lazy = new long[4 * n]; build(arr, 0, 0, n - 1); } // 递归构建线段树 private void build(int[] arr, int node, int start, int end) { if (start == end) { tree[node] = arr[start]; } else { int mid = (start + end) / 2; build(arr, 2 * node + 1, start, mid); build(arr, 2 * node + 2, mid + 1, end); tree[node] = tree[2 * node + 1] + tree[2 * node + 2]; } } // 区间查询:[l, r] public long query(int l, int r) { return query(0, 0, n - 1, l, r); } private long query(int node, int start, int end, int l, int r) { // 先处理懒标记 if (lazy[node] != 0) { tree[node] += (end - start + 1) * lazy[node]; if (start != end) { lazy[2 * node + 1] += lazy[node]; lazy[2 * node + 2] += lazy[node]; } lazy[node] = 0; } if (r < start || end < l) { return 0; // 无交集 } if (l <= start && end <= r) { return tree[node]; // 完全包含 } int mid = (start + end) / 2; long leftSum = query(2 * node + 1, start, mid, l, r); long rightSum = query(2 * node + 2, mid + 1, end, l, r); return leftSum + rightSum; } // 区间更新:将 [l, r] 每个元素加上 val public void update(int l, int r, int val) { update(0, 0, n - 1, l, r, val); } private void update(int node, int start, int end, int l, int r, int val) { // 先处理当前节点的懒标记 if (lazy[node] != 0) { tree[node] += (end - start + 1) * lazy[node]; if (start != end) { lazy[2 * node + 1] += lazy[node]; lazy[2 * node + 2] += lazy[node]; } lazy[node] = 0; } if (r < start || end < l) { return; // 无交集 } if (l <= start && end <= r) { // 当前区间完全被覆盖 tree[node] += (long)(end - start + 1) * val; if (start != end) { lazy[2 * node + 1] += val; lazy[2 * node + 2] += val; } return; } int mid = (start + end) / 2; update(2 * node + 1, start, mid, l, r, val); update(2 * node + 2, mid + 1, end, l, r, val); tree[node] = tree[2 * node + 1] + tree[2 * node + 2]; }}下面是一个简单的使用示例:
public class Main { public static void main(String[] args) { int[] arr = {1, 3, 5, 7, 9, 11}; SegmentTree st = new SegmentTree(arr); // 查询区间 [1, 3] 的和:3 + 5 + 7 = 15 System.out.println(st.query(1, 3)); // 输出 15 // 将区间 [0, 2] 每个元素加 2 st.update(0, 2, 2); // 再次查询 [1, 3]:(3+2) + (5+2) + 7 = 5 + 7 + 7 = 19 System.out.println(st.query(1, 3)); // 输出 19 }}如果没有懒标记,每次区间更新都要递归到所有叶子节点,时间复杂度退化为 O(n)。而懒标记的思想是“延迟更新”——只在真正需要访问子节点时才把更新操作下传,从而保证每次操作都是 O(log n)。
通过本教程,你已经掌握了:
线段树是解决区间问题的强大工具,在 LeetCode、Codeforces 等平台的算法题中频繁出现。建议你动手敲一遍代码,加深理解!
希望这篇教程能帮助你顺利入门线段树。如果你觉得有用,欢迎分享给更多学习 Java语言线段树 的朋友!
本文由主机测评网于2025-12-04发表在主机测评网_免费VPS_免费云服务器_免费独立服务器,如有疑问,请联系我们。
本文链接:https://www.vpshk.cn/2025122733.html