藍橋杯 算法 線段樹 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;
	}

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章