1. 程式人生 > 其它 >【隨筆淺談】splay 時間複雜度簡要分析

【隨筆淺談】splay 時間複雜度簡要分析

splay 時間複雜度簡要分析。

勢能分析

在資料結構問題中,我們往往難以估計第 \(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')\)

分別表示伸展 \(x\) 前和伸展 \(x\) 後節點 \(x\) 的勢能值。

雙旋 splay 的三種旋轉

來回顧一下雙旋 splay 的三種旋轉:

  1. \(x\) 的父親 \(p\) 是根節點時,直接 zig/zag \(x\)

  2. \(x\) 和上兩代祖先位於一條鏈上:先 zig/zag \(p\),再 zig/zag \(x\)

  3. \(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.