1. 程式人生 > >可持久化數據結構

可持久化數據結構

關系 大小 特定 復用 時間復雜度 樹狀 缺點 沒有 一個

  我們經常會遇到這樣的問題:我們需要維護一個數據結構,我們可以修改單一結點的值,查詢單一結點的值,但是最關鍵的是我們可能還需要回退之前做過的某些操作。這裏回退是指回到未做這些操作之前的狀態。

  在無回退操作的情況下,我們有大把的數據結構可供選擇來解決這些問題。但是一旦涉及到回退操作,選擇就少的多了。我們將支持回退操作的數據結構稱為可持久化數據結構。

  稍微思考一下如何可以在原來數據結構的基礎上使其變得可持久化,有一個很簡單的方案。我們每次操作都將重新建立一個新的數據結構,並將之前的操作都先在其上執行一次,之後執行該次操作。我們按操作執行順序將這些數據結構維護成一個序列S,此時S[0]表示未經任何操作的初始數據結構。對於i>0,S[i]表示在S[0]的基礎上執行過序號1到i的所有操作後得到的新的數據結構。在這樣的做法下,我們稱S[i]為版本i,回退操作等價於切換到某個特定版本。若操作i表示切換為版本j,那麽我們可以直接將S[i]設置為S[j]的克隆。

  上面提到的做法下很容易發現可以使得任意數據結構都可以支持回退操作,但是缺點也是非常明顯,空間和時間的復雜度都奇高。每一次操作都需要累加之前操作的時間復雜度,空間也是,我們為了保存各個版本需要耗費大量的內存。

  先說明時間復雜度的優化,對於i號操作,我們完全可以直接克隆版本S[i-1]並在其上執行i號操作,這樣時間復雜度基本上就向空間復雜度看齊了。下面我們就可以專註於空間復雜度的優化(對應的也就是時間復雜度的優化)。

  數據結構是用於保存數據的,我們將其保存數據的單元稱為結點,我們可以利用結點來刻畫整個數據結構的骨架。數據結構基本分為兩類,一類是穩定的,一類是不穩定的。穩定的數據結構,其特定是在修改的結點的值之後不會改變結點之間的關系,而不穩定的數據結構在結點值變更後需要重新維護結點之間的關聯。穩定的數據結構有線段樹,後綴數組,前綴樹等等,不穩定的數據結構主要就是各種二叉平衡樹。對於穩定的樹狀結構,若孩子沒有保存指向父結點的指針,即由父親負責記錄所有的孩子,我們很容易發現,當我們對某個結點更改時(修改值,新增,刪除等操作),我們只需要同時修改該結點的所有祖先結點即可,那我們是不是也可以只克隆這些結點而非整個數據結構呢?答案是肯定的。由於父親維護孩子,因此一個孩子允許有多個父親,故所有沒有被直接影響的結點都可以繼續復用。我們將部分樹狀數據結構(特定是穩定和父親維護父子關系)的一次操作的空間復雜度優化到了O(h),其中h是樹狀數據結構的高度。

  當我們將上面的想法作用到線段樹時,就得到了常說的主席樹。其高度為O(log2(n)),其中n為線段樹維護的區間大小,同時其時間和空間復雜度均為O(log2(n))。

可持久化數據結構