之前一直听说线段树是一个很高级很难的数据结构,今天简单了解了下, 感觉就是二叉树加几个全局变量啊,原来这么easy?(开个玩笑)
简单说几个特点,
1. 每个节点除了存放left,right指针之外,还存着一个范围(这个范围一般是构建线段树之前数组的索引范围), 就是以当前节点为根的情况下,对自己下面所有节点的求交集, 还可以根据你的需求 加一些别的特殊字段,sum,max,min等等
2. 数据集都存在叶子节点上,非叶子节点只做归纳总结
一般还有几个操作
1. 初始化,就是把一个数组初始化成一个线段树(关键是修改你需求中的特殊字段 sum等)
2. 修改某一个索引处的数值,同时保证他的祖先节点的特殊字段是对的
3. 求某一个索引范围内的信息
整体思路是分治,递归求解。
leetcode 307 是一个典型的线段树
给定一个整数数组 nums,求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和,包含 i, j 两点。
update(i, val) 函数可以通过将下标为 i 的数值更新为 val,从而对数列进行修改。
示例:
Given nums = [1, 3, 5] sumRange(0, 2) -> 9 update(1, 2) sumRange(0, 2) -> 8
说明:
- 数组仅可以在 update 函数下进行修改。
- 你可以假设 update 函数与 sumRange 函数的调用次数是均匀分布的。
这道题,看到题之后,最朴素的解法就是每次都从i到j遍历,更新就直接更新。 这样更新的时间复杂度是o1, 求和的时间复杂度是on
这个时候,思考下有没有别的解决办法, 我们维护一个前n项和的数组,sum(n) = 0 到 n的累加和, range(i,j)= sum(j) - sum(i)
这样的话range的时间复杂度就是o1了, 但是更新的时候你需要更新很多个前n项和,这样更新就成on了, 有没有更优的解法呢?
用segment tree,这样的话更新和查找就都是logn的时间复杂度了,这样就优了不少, 比如n等于1000, 那么一个o1的时间复杂度一个on的时间复杂度那么总共就是1000+
,但是两个都是logn的话,就是20, 如果n比较大的话那么差别就更大了。
talk is cheap, 上代码
class NumArray { class SegmentTree { int start; int end; int sum; SegmentTree left; SegmentTree right; SegmentTree(int s, int e, int sum_temp, SegmentTree l, SegmentTree r) { start = s; end = e; sum = sum_temp; left = l; right = r; } } int[] n; SegmentTree head; public NumArray(int[] nums) { n = nums; head = init(nums, 0, nums.length - 1); } SegmentTree init (int[] nums, int start, int end) { if (start > end) return null; if (start == end) { return new SegmentTree(start, end, nums[start], null, null); } int mid = (start + end)/2; SegmentTree left = init(nums, start, mid); SegmentTree right = init(nums, mid + 1, end); return new SegmentTree(start, end, left.sum + right.sum, left, right); } public void update(int i, int val) { runUpdate(head, i, val); } void runUpdate(SegmentTree root, int i, int val) { if (root.start == root.end) { root.sum = val; return; } int mid = (root.start + root.end)/2; if (i <= mid) { runUpdate(root.left, i, val); } else { runUpdate(root.right, i, val); } root.sum = root.left.sum + root.right.sum; } public int sumRange(int i, int j) { return runSumRange(head, i, j); } int runSumRange(SegmentTree root, int i, int j) { if (root.start == i&&root.end == j) { return root.sum; } int mid = (root.start + root.end)/2; if (j <= mid) { return runSumRange(root.left, i, j); } if (i > mid) { return runSumRange(root.right, i, j); } return runSumRange(root.left, i, mid) + runSumRange(root.right, mid + 1, j); } }
原文地址:https://www.cnblogs.com/tobemaster/p/10474420.html