『ACM-数据结构』信息竞赛进阶指南--线段树
生活随笔
收集整理的這篇文章主要介紹了
『ACM-数据结构』信息竞赛进阶指南--线段树
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
我們主要是講代碼實現,不是講基本原理!
什么是線段樹?
線段樹是一種二叉搜索樹,與區間樹相似,它將一個區間劃分成一些單元區間,每個單元區間對應線段樹中的一個葉結點。
使用線段樹可以快速的查找某一個節點在若干條線段中出現的次數,時間復雜度為O(logN)。而未優化的空間復雜度為2N,實際應用時一般還要開4N的數組以免越界,因此有時需要離散化讓空間壓縮。
定義
線段樹是一種二叉搜索樹,與區間樹相似,它將一個區間劃分成一些單元區間,每個單元區間對應線段樹中的一個葉結點。 [1]
對于線段樹中的每一個非葉子節點[a,b],它的左兒子表示的區間為[a,(a+b)/2],右兒子表示的區間為[(a+b)/2+1,b]。因此線段樹是平衡二叉樹,最后的子節點數目為N,即整個線段區間的長度。
使用線段樹可以快速的查找某一個節點在若干條線段中出現的次數,時間復雜度為O(logN)。而未優化的空間復雜度為2N,因此有時需要離散化讓空間壓縮
線段樹的適用范圍:
線段樹的適用范圍很廣,可以在線維護修改以及查詢區間上的最值,求和。更可以擴充到二維線段樹(矩陣樹)和三維線段樹(空間樹)。對于一維線段樹來說,每次更新以及查詢的時間復雜度為O(logN)。
//模板是用李煜東的 struct SegmentTree {int l, r;int dat; } t[SIZE * 4]; // struct數組存儲線段樹void build(int p, int l, int r) {t[p].l = l, t[p].r = r; // 節點p代表區間[l,r]if (l == r) { t[p].dat = a[l]; return; } // 葉節點int mid = (l + r) / 2; // 折半build(p*2, l, mid); // 左子節點[l,mid],編號p*2build(p*2+1, mid+1, r); // 右子節點[mid+1,r],編號p*2+1t[p].dat = max(t[p*2].dat, t[p*2+1].dat); // 從下往上傳遞信息 }build(1, 1, n); // 調用入口void change(int p, int x, int v) {if (t[p].l == t[p].r) { t[p].dat = v; return; } // 找到葉節點int mid = (t[p].l + t[p].r) / 2;if (x <= mid) change(p*2, x, v); // x屬于左半區間else change(p*2+1, x, v); // x屬于右半區間t[p].dat = max(t[p*2].dat, t[p*2+1].dat); // 從下往上更新信息 }change(1, x, v); // 調用入口int ask(int p, int l, int r) {if (l <= t[p].l && r >= t[p].r) return t[p].dat; // 完全包含,直接返回int mid = (t[p].l + t[p].r) / 2;int val = 0;if (l <= mid) val = max(val, ask(p*2, l, r)); // 左子節點有重疊if (r > mid) val = max(val, ask(p*2+1, l, r)); // 右子節點有重疊return val; }cout << ask(1, l, r) << endl; // 調用入口// 動態開點的線段樹 struct SegmentTree {int lc, rc; // 左右子節點的編號int dat; } tr[SIZE * 2]; int root, tot;int build() { // 新建一個節點tot++;tr[tot].lc = tr[tot].rc = tr[tot].dat = 0;return tot; }// 在main函數中 tot = 0; root = build(); // 根節點// 單點修改,在val位置加delta,維護區間最大值 void insert(int p, int l, int r, int val, int delta) {if (l == r) {tr[p].dat += delta;return;}int mid = (l + r) >> 1; // 代表的區間[l,r]作為遞歸參數傳遞if (val <= mid) {if (!tr[p].lc) tr[p].lc = build(); // 左子樹不存在,動態開點insert(tr[p].lc, l, mid, val, delta);}else {if (!tr[p].rc) tr[p].rc = build(); // 右子樹不存在,動態開點insert(tr[p].rc, mid + 1, r, val, delta);}tr[p].dat = max(tr[tr[p].lc].dat, tr[tr[p].rc].dat); }// 調用 insert(root, 1, n, val, delta);// 合并兩棵線段樹 int merge(int p, int q, int l, int r) {if (!p) return q; // p,q之一為空if (!q) return p;if (l == r) { // 到達葉子tr[p].dat += tr[q].dat;return p;}int mid = (l + r) >> 1;tr[p].lc = merge(tr[p].lc, tr[q].lc, l, mid); // 遞歸合并左子樹tr[p].rc = merge(tr[p].rc, tr[q].rc, mid + 1, r); // 遞歸合并右子樹tr[p].dat = max(tr[tr[p].lc].dat, tr[tr[p].rc].dat); // 更新最值return p; // 以p為合并后的節點,相當于刪除q }總結
以上是生活随笔為你收集整理的『ACM-数据结构』信息竞赛进阶指南--线段树的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 『ACM-算法-数据结构』信息竞赛进阶指
- 下一篇: C语言入门经典题目及其答案