很简单,线段树这东西就是为了节省在修改数组后,为了简便的获得区间值。
如数组
int [] List={1,2,3,4}; 很显然 ,正常人的思维是 要获取那一段的值,就把这一段加起来 如 要获取区间
[2,3] 就把list[2]+list[3]加起来。时间复杂度很显然是O(n) 如果要频繁的修改,多次修改,很显然不可取。
很显然,一个数组可以拆分为任意个连续数组的集合。如果中一个数组中间,将该数组分开,那么可以保证所有分开的
数组区间是连续的。
既:int mid= (right+left)/2 (left 为一个数字左端开始的坐标,right 为该数组右端开始的坐标),这里不用说你肯定知道
需要查询一个区间段的时候,形如[1-3]。如在list中查询区间2->3的值。只需要选取部分拆分开的数组,既可知道
该区间的值。
为什么是部分?上面已经说明了,只要一个数组及其子数组是从数组中间分开的,那么其区间段肯定是连续的。
[1,2,3,4]
左分[1,2] 右分[3,4]
左分[1] 右分[2] 左分[3] 右分[4]
很显然,一个区间段,肯定是属于线段树一个区间到三个区间的,自己想想[0-3] ,[1-3],[2-3]的区别。
线段树二分的原理近似于二叉树,层次也只会有log2N层次。既每次查询任意区间的时间复杂度为Log2N。
按照上面的[1,2,3,4]图,可以看出,修改任意一个元素后,会发生改变的肯定是他的超集。所以可以利用线段树的
划分性质,对线段树进行dfs,到底后,线段树节点所代表的值就是未被修改的值,将该节点修改为新值。在层层返回
的过程中,代码会自动的将线段树路径进行修改。修改时间复杂度等同于查询复杂度,其实就相当于是查询了一个区间
[k,k]的值
菜鸟肯定会对为什么线段树数组的大小开为4N,很疑惑。如果不是专门搞数学的,别去研究了,推导归纳起来有点复杂
还有就是如何证明 子节点的位置 leftNode=2*node +1 rightNode=2*node+2 自己动脑子想想,比赛考研都不考
我就不打证明了。
下面这个题,因为题目特殊性不需要build函数,自己找个简单题目练习一下就OK,其实builde就是update改一下。写这个代码主要看你的递归能力。
public class 计算右侧小于当前元素的个数2 {
static int[] tree;
public List<Integer> countSmaller(int[] nums) {
int min = Integer.MAX_VALUE;
int max = Integer.MIN_VALUE;
for (int i = 0; i < nums.length; i++) {
min = Math.min(min, nums[i]);
max = Math.max(max, nums[i]);
}
tree = new int[4 * (max - min + 1)];
Stack<Integer> stack = new Stack<Integer>();
for (int i = nums.length - 1; i >= 0; i--) {
// 查询右侧
stack.push(query(min, max, min, nums[i] - 1, 0));
// 把该数字加入
update(min, max, 0, nums[i]);
}
List<Integer >list=new ArrayList<Integer>();
while(!stack.isEmpty())list.add(stack.pop());
return list;
}
public static void update(int start, int end, int node, int index) {
//错误就出在这里
if (start == end&& end==index ) {
tree[node]+=1;
return;
}
int mid = (start + end) >> 1;
if (mid >= index) {
update(start, mid, node * 2+1, index);
} else {
update(mid + 1, end, node * 2 + 2, index);
}
tree[node] = tree[node * 2+1] + tree[node * 2 + 2];
}
public static int query(int start, int end, int l, int r, int node) {
if (l > r)
return 0;
// 如果查询范围内没有,就直接返回
if (l > end || r < start)
return 0;
// 如果该区间已经被包含在线段树内,直接返回
else if (l <= start && r >= end) {
return tree[node];
}
int mid = (start + end) >> 1;
int rValue = query(start, mid, l, r, node * 2+1);
int lValue = query(mid + 1, end, l, r, node * 2 + 2);
return rValue + lValue;
}
}