线段树这篇就够了! 线段树 LeetCode315. 计算右侧小于当前元素的个数 带模板 java解答

线段树是什么东西

一句话定义

很简单,线段树这东西就是为了节省在修改数组后,为了简便的获得区间值。
如数组 
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]的值

线段树代码- LeetCode315. 计算右侧小于当前元素的个数

菜鸟肯定会对为什么线段树数组的大小开为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;
	}

}

你可能感兴趣的:(线段树,蓝桥杯算法)