數據結構的合並
引言
有一類經典的數據結構問題,要求高效地支持以下幾種操作:
1.新建一個數據結構
2.將兩個數據結構的信息合並(要求合並操作滿足交換律、結合律)
3.在數據結構中查詢某些信息
這類問題有時需要根據數據結構的特性設計操作,同時也存在一類較為通用的解決方法。
合並操作可以看做批量地在數據結構中添加信息,而一般的插入操作則是合並的特例
定義與約定
設n表示問題中新建的數據結構個數
|A|表示數據結構A由幾個數據結構合並得到
A+B表示合並A,B得到的數據結構,因此有|A+B|=|A|+|B|
直接合並
當合並兩個數據結構時,可以將其中一個數據結構的信息逐一插入到另一個數據結構中,總共需要進行O(n^2)次插入操作。
基於某些特殊性質的合並
森林實現的並查集可以直接通過修改父親合並
無旋轉treap可以基於分裂操作遞歸合並,若兩樹值域區間不相交也可以用類似斜堆的合並方式
各種可並堆也設計了相應的方式進行高效的合並
此類合並一般非常高效,一部分還能同時在非均攤的意義下保證很低的合並復雜度,但一般難以推廣。
啟發式合並
若在合並A,B時,令|A|<=|B|,將A拆散並逐一插入B,由於每次插入會使一個元素所在數據結構增大至少一倍,可以將插入操作的次數降至O(nlogn)。
平衡樹啟發式合並是一個常見的例子。
基於相同結構的合並
對於兩個結構相同的數據結構,可以考慮將兩個數據結構重疊起來,合並重疊部分的信息。
一個簡單的例子是trie的合並,將兩棵待合並的trie重疊起來,從根開始dfs求出重疊部分並合並相關信息。合並的時間復雜度與合並時重疊部分的大小有關,由於重疊部分合並後總點數減少了,可以保證總的時間復雜度與合並時減少的點數同階。
對於線段樹或k-d樹,如果采用相同的區間劃分方式,也可以使數據結構的結構相同,從而可以應用這個方法。
此方法一般優於直接進行啟發式合並。
例題 2014年集訓隊互測 bzoj3615 MSS
題目大意 維護一些平面上的點集,支持按某一維坐標分割點集,合並兩個點集,對一個點集的所有點點權加上一個數,詢問一個點集的點權和、最小/大值。
對給出的點建立二維k-d樹,建樹後將每個點到根的路徑提取出來得到n棵只包含一個點的相同結構的k-d樹。合並操作可以很方便地遞歸進行,點權的維護可以仿照線段樹打標記實現。分裂操作可以看作合並的逆操作,由於k-d樹的結構,可以保證分裂時增加點數為O(n^(1/2))。
基於二進制分組的合並
某些時候,不能高效地在數據結構中插入一條信息,這時候便很難應用前文所提及的方法。如果對A+B的一次查詢可以轉化為對A,B分別查詢,那麽可以平衡合並和查詢的時間復雜度,在合並時不完全合並,以增加查詢的復雜度為代價減少合並次數。
對於數據結構A,可以將|A|表示為一些互不相同的2的非負整數次冪之和(至多分為O(log|A|)個數),並將信息分別維護,查詢時在O(log|A|)個部分內分別查詢。在合並時,仿照二進制加法的進位過程,合並對應的部分。這樣一個元素每參與一次合並,所在數據結構的大小增大一倍,若合並A,B的時間復雜度為O(|A|+|B|),則合並操作的總時間復雜度為O(nlogn),優於直接對二進制分組套用啟發式合並。
數據結構的合並