UP | HOME

左偏樹(leftist tree)

有些資料結構可以有效的存取任意元素,但有時候我們需要的只是高效存取最小的元素。 支援這種存取模式的資料結構就叫做 priority queue 或是 heap。 而 heap 經常用 heap-ordered tree 實作。

1. Heap-ordered

所謂 heap-ordered,就是每個節點的元素不大於其子節點的元素。 在這個排序下,樹的最小元素總是在根節點。

2. 左偏樹

左偏樹的定義是滿足 leftist property 又符合 heap-ordered 的二元樹。

2.1. leftist property

任何左節點的 rank 都大於等於右邊兄弟節點的 rank。 rank 定義成 rightmost path 到空節點的距離。

3. 程式

左偏樹的核心結構定義是

\data Heap (A : \Set)
  | E
  | T Nat A (Heap A) (Heap A)

合併樹是左偏樹下最重要的實作,關鍵是保持兩顆樹符合左偏樹的定義

\func merge {A : Ord} (h1 h2 : Heap A) : Heap A
  | E, h => h
  | h, E => h
  | T _ x a1 b1 \as h1, T _ y a2 b2 \as h2 =>
    \case x <= y \with {
      | true => make-tree x a1 (merge b1 h2)
      | false => make-tree y a2 (merge h1 b2)
    }
  \where
    \func make-tree {A : \Set} (x : A) (left right : Heap A) : Heap A =>
      \case rank right <= rank left \with {
        | true => T (rank right + 1) x left right
        | false => T (rank left + 1) x right left
      }
    \func rank {A : \Set} (h : Heap A) : Nat
      | E => 0
      | T r _ _ _ => r

插入元素可以用 merge 實作

\func insert {A : Ord} (x : A) (h : Heap A) : Heap A => merge (T 1 x E E) h

搜尋最小元素或是刪除最小元素都可以仰賴左偏樹的特性, 這裡找不到最小元素時是簡單的用 Maybe 型別包藏起來。

\func find-min {A : \Set} (h : Heap A) : Maybe A
  | E => nothing
  | T _ e _ _ => just e
\func delete-min {A : Ord} (h : Heap A) : Heap A
  | E => E
  | T _ _ l r => merge l r

檢查樹是不是空的非常的容易

\func empty? {A : \Set} (h : Heap A) : Bool
  | E => true
  | _ => false

3.1. Appendix

由於 Arend 並沒有提供程式上需要的偏序 Functor(倒是有證明用的偏序 Functor),所以需要自己定義一個

\class Ord (A : \Set)
  | \infix 6 <= : A -> A -> Bool

\func Nat<= (x y : Nat) : Bool
  | 0, n => true
  | n , 0 => false
  | suc n, suc m => Nat<= n m
\instance Nat-Ord : Ord Nat
  | <= => Nat<=

另外需要引入一些程式庫

\import Algebra.Monoid
\import Arith.Nat
\import Data.Bool
\import Data.Maybe
Date: 2022-10-30 Sun 00:00
Author: Lîm Tsú-thuàn