【隨筆淺談】splay 時間複雜度簡要分析
阿新 • • 發佈:2021-08-30
splay 時間複雜度簡要分析。
的複雜度就可以求出總的實際時間了。
分別表示伸展 \(x\) 前和伸展 \(x\) 後節點 \(x\) 的勢能值。
勢能分析
在資料結構問題中,我們往往難以估計第 \(i\) 次的實際時間開銷 \(t_i\)。
所以我們要引入一些勢能分析的概念:
- 設 \(\phi_i\) 表示:第 \(i\) 次操作過後,資料結構的勢能值。
- 記 \(a_i = t_i + \phi_i - \phi_{i - 1}\),即第 \(i\) 次操作的均攤時間。
注意:確定勢能值的勢函式是需要我們自己確定的,尋找一個優秀的勢函式往往可以更容易解決問題。
假設執行了 \(m\) 次操作,那麼總的實際時間為:
\[\sum\limits_{1 \leq i \leq m} t_i = \sum\limits_{1 \leq i \leq m} a_i + \phi_0 - \phi_m \]所以知道了 \(a_i, \phi\)
splay 的複雜度分析
在 splay 中,我們取這樣的一個勢函式:
- 設當前狀態下,節點 \(x\) 的勢能值 \(F(x) = \log \text{size}_x\)。
- 設當前狀態下,整棵 splay 的勢能值 \(\phi = \sum\limits_{1 \leq i \leq n} F(i)\)。
接下來要證明的結論是:
\[\begin{aligned}a_i & \leq 3(F(x') - F(x)) + 1 \\& = \mathcal{O}\left(\log \frac{n}{\text{size}_x}\right) = \mathcal{O}(\log n)\end{aligned} \]其中 \(F(x), F(x')\)
雙旋 splay 的三種旋轉
來回顧一下雙旋 splay 的三種旋轉:
-
當 \(x\) 的父親 \(p\) 是根節點時,直接 zig/zag \(x\)。
-
當 \(x\) 和上兩代祖先位於一條鏈上:先 zig/zag \(p\),再 zig/zag \(x\)。
-
當 \(x\) 和上兩代祖先是分叉時:先 zig/zag \(x\),再 zig/zag \(x\)。
第一種旋轉的均攤分析(以 zig 為例)
簡單分析一下:
-
時間開銷:旋轉了一次。
-
勢能變化:子樹大小隻有節點 \(x, y\) 發生了變化,故只有節點 \(x, y\)
所以可以得到:
\[\begin{aligned}a_{\text{zig}} & = 1 + F(x') + F(y') - F(x) - F(y) \\& = 1 + F(y') - F(x)\end{aligned} \]適當放縮得:
\[a_{\text{zig}} \leq 3(F(x') - F(x)) + 1 \]第二種旋轉的均攤分析(以 zig-zig 為例)
簡單分析一下:
- 時間開銷:旋轉了兩次。
- 勢能變化:子樹大小隻有節點 \(x, y, z\) 發生了變化,故只有節點 \(x, y, z\) 的勢能值發生了變化。
所以可以得到:
\[\begin{aligned}a_{\text{zig-zig}} & = 2 + F(x') + F(y') + F(z') - F(x) - F(y) - F(z) \\& = 2 + F(y') + F(z') - F(x) - F(y)\end{aligned} \]然後你發現這個多出來的 \(2\) 非常令人不爽。
注意到:
\[F(x) + F(z') - 2 \cdot F(x') \leq \log \frac{\text{size}_x \cdot \text{size}_{z'}}{\text{size}^2_{x'}} \leq -2 \](將上式對數的底數看成 \(2\),用均值不等式即可證明,大家都會。)
然後將這兩個式子合併可以得到:
\[a_{\text{zig-zig}} \leq 2 \cdot F(x') + F(y') - 2 \cdot F(x) - F(y) \]適當放縮得:
\[a_{\text{zig-zig}} \leq 3(F(x') - F(x)) \]第三種旋轉的均攤分析(以 zig-zag 為例)
簡單分析一下:
- 時間開銷:旋轉了兩次。
- 勢能變化:子樹大小隻有節點 \(x, y, z\) 發生了變化,故只有節點 \(x, y, z\) 的勢能值發生了變化。
所以可以得到:
\[\begin{aligned}a_{\text{zig-zag}} & = 2 + F(x') + F(y') + F(z') - F(x) - F(y) - F(z) \\& = 2 + F(y') + F(z') - F(x) - F(y)\end{aligned} \]注意到:
\[F(x) + F(z') - 2 \cdot F(x') \leq \log \frac{\text{size}_x \cdot \text{size}_{z'}}{\text{size}^2_{x'}} \leq -2 \]然後將這兩個式子合併可以得到:
\[a_{\text{zig-zag}} \leq 2 \cdot F(x') + F(y') - 2 \cdot F(x) - F(y) \]適當放縮得:
\[a_{\text{zig-zag}} \leq 3(F(x') - F(x)) \]根據上述證明,我們可以知道,在一次操作中,伸展一個節點 \(x\) 的時候,將每次旋轉的貢獻式子連線起來,消除相鄰的項,然後就可以得到:
\[\begin{aligned}a_i & \leq 3(F(x') - F(x)) + 1 \\& = \mathcal{O}\left(\log \frac{n}{\text{size}_x}\right) = \mathcal{O}(\log n)\end{aligned} \]然後你會發現:
- 因為 \(a_i = \mathcal{O}(\log n)\),所以 \(\sum\limits_{1 \leq i \leq m} a_i = \mathcal{O}(m \log n)\)。
- 因為 \(0 \leq \phi \leq n \log n\),所以 \(\phi_0 - \phi_m = \mathcal{O}(n \log n)\)。
於是 splay 的時間複雜度即為 \(\mathcal{O}((n + m) \log n)\)。
keep the love forever.