lean
import SFLean4.VFA.Perm
import SFLean4.VFA.Maps
import SFLean4.VFA.Sort
namespace VFA.SearchTree
open VFA.Perm
open VFA.Maps
open VFA.Sort《软件基础》(Software Foundations) 卷三:函数式算法验证 (VFA)
第九章:二叉搜索树 (SearchTree: Binary Search Trees)
在前面章节中,我们分别使用键值对列表(《逻辑基础》)和高阶函数(Maps)实现了字典映射。 但这两种实现各有利弊:
- 列表查找的时间复杂度与键值对数量呈线性关系
; - 高阶函数不支持枚举元素键集,且难以检查有限性。
如果键集合存在全序关系(例如自然数上的
本章将系统性完成:
- 持久化函数式 BST 的实现(纯函数式树节点插入与查询);
- BST 不变量(BST Invariant)的形式化定义;
- **代数规范(Algebraic Specification)**对 BST 操作的刻画;
- 展平为有序关联列表(Elements)的完备性与正确性双向证明;
- 模型化规范(Model-based Specification):证明 BST 与抽象字典映射(Partial Map)的仿真对应。
1. BST 的持久化函数式实现 (BST Implementation)
lean
/-- 树中存储的键类型统一采用自然数 -/
abbrev key : Type := Nat
/-- 二叉搜索树的归纳数据类型:
- `E`:空树(Empty Tree);
- `T l k v r`:存储键 `k` 与值 `v` 的节点,左子树为 `l`,右子树为 `r`。 -/
inductive tree (V : Type) : Type where
| E : tree V
| T (l : tree V) (k : key) (v : V) (r : tree V) : tree V
/-- 空树常量 -/
def empty_tree {V : Type} : tree V :=
tree.E
/-- 查询键 `x` 是否在树 `t` 中被绑定 -/
def bound {V : Type} (x : key) (t : tree V) : Bool :=
match t with
| tree.E => false
| tree.T l y _ r =>
if x < y then bound x l
else if x > y then bound x r
else true
/-- 在树 `t` 中查找键 `x` 对应的值;若键未绑定,则返回默认值 `d` -/
def lookup {V : Type} (d : V) (x : key) (t : tree V) : V :=
match t with
| tree.E => d
| tree.T l y v r =>
if x < y then lookup d x l
else if x > y then lookup d x r
else v
/-- 持久化插入:向树中加入新的键值绑定 `(x, v)`;若键已存在则替换其值 -/
def insert {V : Type} (x : key) (v : V) (t : tree V) : tree V :=
match t with
| tree.E => tree.T tree.E x v tree.E
| tree.T l y v' r =>
if x < y then tree.T (insert x v l) y v' r
else if x > y then tree.T l y v' (insert x v r)
else tree.T l x v r树操作的单元测试
lean
def ex_tree : tree String :=
tree.T (tree.T tree.E 2 "two" tree.E) 4 "four" (tree.T tree.E 5 "five" tree.E)
example : insert 5 "five" (insert 2 "two" (insert 4 "four" empty_tree)) = ex_tree := by
rfl
example : lookup "" 5 ex_tree = "five" := by rfl
example : lookup "" 3 ex_tree = "" := by rfl
example : bound 3 ex_tree = false := by rfl2. BST 不变量 (BST Invariant)
二叉搜索树的核心在于其结构不变量: 对于每个非空节点 T l k v r,左子树 l 中的所有键均严格小于 k, 而右子树 r 中的所有键均严格大于 k,且左右子树自身也均满足 BST 不变量。
lean
/-- 谓词全称遍历:命题 `P` 对二叉树 `t` 的每一个节点均成立 -/
def ForallT {V : Type} (P : key → V → Prop) (t : tree V) : Prop :=
match t with
| tree.E => True
| tree.T l k v r => P k v ∧ ForallT P l ∧ ForallT P r
/-- 二叉搜索树不变量的归纳定义 -/
inductive BST {V : Type} : tree V → Prop where
| BST_E : BST tree.E
| BST_T (l : tree V) (x : key) (v : V) (r : tree V) :
ForallT (fun y _ => y < x) l →
ForallT (fun y _ => y > x) r →
BST l →
BST r →
BST (tree.T l x v r)
/-- #### 练习:1 星 (empty_tree_BST)
证明空树必然满足 BST 不变量。 -/
theorem empty_tree_BST : ∀ (V : Type), BST (@empty_tree V) := by
sorry
/-- #### 练习:1.5 星 (ForallT_insert)
引理:向树中插入新元素后,保全树节点谓词。 -/
theorem ForallT_insert : ∀ (V : Type) (P : key → V → Prop) (t : tree V),
ForallT P t →
∀ (k : key) (v : V), P k v → ForallT P (insert k v t) := by
sorry
/-- #### 练习:1.5 星 (insert_BST)
核心定理:对满足 BST 不变量的树执行插入,得到的新树仍然满足 BST 不变量。 -/
theorem insert_BST : ∀ (V : Type) (k : key) (v : V) (t : tree V),
BST t → BST (insert k v t) := by
sorry3. BST 操作的代数规范 (Algebraic Specification)
lean
/-- 在空树中查找任何键均返回默认值 -/
theorem lookup_empty : ∀ (V : Type) (d : V) (k : key),
lookup d k empty_tree = d := by
intro V d k
rfl
/-- 刚刚插入的键立即查询必能命中对应的值 -/
theorem lookup_insert_eq : ∀ (V : Type) (t : tree V) (d : V) (k : key) (v : V),
lookup d k (insert k v t) = v := by
sorry
/-- 插入键 `k` 不会影响对不同键 `k'` 的查询结果 -/
theorem lookup_insert_neq : ∀ (V : Type) (t : tree V) (d : V) (k k' : key) (v : V),
k ≠ k' → lookup d k' (insert k v t) = lookup d k' t := by
sorry
/-- #### 练习:1 星,选做 (bound_default)
证明若 `bound` 返回 false,则 `lookup` 必定返回默认值。 -/
theorem bound_default : ∀ (V : Type) (k : key) (d : V) (t : tree V),
bound k t = false → lookup d k t = d := by
sorry
/-- #### 练习:2 星,选做 (lookup_insert_shadow) -/
theorem lookup_insert_shadow : ∀ (V : Type) (t : tree V) (v v' d : V) (k k' : key),
lookup d k' (insert k v (insert k v' t)) = lookup d k' (insert k v t) := by
sorry
/-- #### 练习:2 星,选做 (lookup_insert_same) -/
theorem lookup_insert_same : ∀ (V : Type) (k k' : key) (d : V) (t : tree V),
lookup d k' (insert k (lookup d k t) t) = lookup d k' t := by
sorry
/-- #### 练习:2 星,选做 (lookup_insert_permute) -/
theorem lookup_insert_permute : ∀ (V : Type) (v1 v2 d : V) (k1 k2 k' : key) (t : tree V),
k1 ≠ k2 →
lookup d k' (insert k1 v1 (insert k2 v2 t)) =
lookup d k' (insert k2 v2 (insert k1 v1 t)) := by
sorry
/-- 树结构层面覆盖插入的直接等式 -/
theorem insert_shadow_equality : ∀ (V : Type) (t : tree V) (k : key) (v v' : V),
insert k v (insert k v' t) = insert k v t := by
sorry
/-- #### 练习:2 星,选做 (direct_equalities_break)
树层面的“重新插入原值”等式反例 -/
theorem insert_same_equality_breaks :
∃ (V : Type) (d : V) (t : tree V) (k : key),
insert k (lookup d k t) t ≠ t := by
sorry
/-- 树层面的“交换插入次序”等式反例 -/
theorem insert_permute_equality_breaks :
∃ (V : Type) (v1 v2 : V) (k1 k2 : key) (t : tree V),
k1 ≠ k2 ∧ insert k1 v1 (insert k2 v2 t) ≠ insert k2 v2 (insert k1 v1 t) := by
sorry4. 将 BST 转换为列表 (Converting a BST to a List)
lean
/-- 中序遍历二叉树生成键值对关联列表 -/
def elements {V : Type} (t : tree V) : List (key × V) :=
match t with
| tree.E => []
| tree.T l k v r => elements l ++ ([(k, v)] : List (key × V)) ++ elements r
example : elements ex_tree = [(2, "two"), (4, "four"), (5, "five")] := by
decide
/-- 元素完备性规约 -/
def elements_complete_spec : Prop :=
∀ (V : Type) (k : key) (v d : V) (t : tree V),
bound k t = true →
lookup d k t = v →
List.Mem (k, v) (elements t)
/-- #### 练习:3 星 (elements_complete)
证明 `elements` 的完备性:凡是树中绑定的元素均会出现在列表中。 -/
theorem elements_complete : elements_complete_spec := by
sorry
/-- 元素正确性规约 -/
def elements_correct_spec : Prop :=
∀ (V : Type) (k : key) (v d : V) (t : tree V),
BST t →
List.Mem (k, v) (elements t) →
bound k t = true ∧ lookup d k t = v
/-- 辅助函数:解柯里化(Uncurry) -/
def uncurry {X Y Z : Type} (f : X → Y → Z) : X × Y → Z :=
fun (a, b) => f a b
/-- #### 练习:2 星 (elements_preserves_forall)
证明树的节点性质转化为列表中元素对性质的保持。 -/
theorem elements_preserves_forall : ∀ (V : Type) (P : key → V → Prop) (t : tree V),
ForallT P t →
Forall (uncurry P) (elements t) := by
sorry
/-- #### 练习:2 星 (elements_preserves_relation)
证明树键与外部分界键的关系在元素列表中得到保持。 -/
theorem elements_preserves_relation :
∀ (V : Type) (k k' : key) (v : V) (t : tree V) (R : key → key → Prop),
ForallT (fun y _ => R y k') t →
List.Mem (k, v) (elements t) →
R k k' := by
sorry
/-- #### 练习:6 星 (elements_correct)
核心定理:证明 `elements` 的正确性。 -/
theorem elements_correct : elements_correct_spec := by
sorry
/-- #### 练习:2 星,进阶 (elements_complete_inverse) -/
theorem elements_complete_inverse :
∀ (V : Type) (k : key) (v : V) (t : tree V),
BST t →
bound k t = false →
¬ List.Mem (k, v) (elements t) := by
sorry
/-- #### 练习:2 星,进阶 (bound_value) -/
theorem bound_value : ∀ (V : Type) (k : key) (t : tree V),
bound k t = true → ∃ v : V, ∀ d : V, lookup d k t = v := by
sorry
/-- #### 练习:4 星,进阶 (elements_correct_inverse) -/
theorem elements_correct_inverse :
∀ (V : Type) (k : key) (t : tree V),
(∀ v : V, ¬ List.Mem (k, v) (elements t)) →
bound k t = false := by
sorry列表的有序性与无重复键
lean
/-- #### 练习:3 星,进阶 (sorted_app)
证明在两个列表间插入中枢元保持全局有序。 -/
theorem sorted_app : ∀ (l1 l2 : List Nat) (x : Nat),
VFA.Sort.sorted l1 →
VFA.Sort.sorted l2 →
Forall (fun n => n < x) l1 →
Forall (fun n => n > x) l2 →
VFA.Sort.sorted (l1 ++ x :: l2) := by
sorry
/-- 抽取关联列表中的所有键 -/
def list_keys {V : Type} (lst : List (key × V)) : List Nat :=
lst.map Prod.fst
/-- #### 练习:6 星,进阶 (sorted_elements)
证明 BST 转换出的列表键必然严格升序排列。 -/
theorem sorted_elements : ∀ (V : Type) (t : tree V),
BST t → VFA.Sort.sorted (list_keys (elements t)) := by
sorry
/-- 双列表无交集判定 -/
def disjoint {X : Type} (l1 l2 : List X) : Prop :=
∀ x : X, List.Mem x l1 → ¬ List.Mem x l2
/-- 无重复列表归纳谓词 -/
inductive NoDup {A : Type} : List A → Prop where
| NoDup_nil : NoDup []
| NoDup_cons (x : A) (l : List A) : ¬ List.Mem x l → NoDup l → NoDup (x :: l)
/-- #### 练习:3 星,进阶,选做 (NoDup_append) -/
theorem NoDup_append : ∀ (X : Type) (l1 l2 : List X),
NoDup l1 → NoDup l2 → disjoint l1 l2 → NoDup (l1 ++ l2) := by
sorry
/-- #### 练习:4 星,进阶,选做 (elements_nodup_keys) -/
theorem elements_nodup_keys : ∀ (V : Type) (t : tree V),
BST t → NoDup (list_keys (elements t)) := by
sorry5. 快速尾递归中序遍历 (Fast Elements Implementation)
lean
/-- 累加器风格的尾递归中序遍历(避免 `++` 带来的二次方开销) -/
def fast_elements_tr {V : Type} (t : tree V) (acc : List (key × V)) : List (key × V) :=
match t with
| tree.E => acc
| tree.T l k v r => fast_elements_tr l ((k, v) :: fast_elements_tr r acc)
/-- 快速遍历顶层调用 -/
def fast_elements {V : Type} (t : tree V) : List (key × V) :=
fast_elements_tr t []
/-- #### 练习:2 星 (fast_elements_tr_helper) -/
theorem fast_elements_tr_helper :
∀ (V : Type) (t : tree V) (lst : List (key × V)),
fast_elements_tr t lst = elements t ++ lst := by
sorry
/-- #### 练习:1 星 (fast_elements_eq_elements)
证明快速遍历与标准遍历结果完全等价。 -/
theorem fast_elements_eq_elements : ∀ (V : Type) (t : tree V),
fast_elements t = elements t := by
sorry
/-- 由等价性即刻推得快速遍历的完全正确性 -/
theorem fast_elements_correct :
∀ (V : Type) (k : key) (v d : V) (t : tree V),
BST t →
List.Mem (k, v) (fast_elements t) →
bound k t = true ∧ lookup d k t = v := by
intro V k v d t hbst hin
rw [fast_elements_eq_elements] at hin
exact elements_correct V k v d t hbst hin6. 模型化规范 (Model-based Specifications)
lean
/-- 将关联列表转化为抽象偏映射 -/
def map_of_list {V : Type} (el : List (key × V)) : partial_map V :=
match el with
| [] => VFA.Maps.empty
| (k, v) :: el' => VFA.Maps.update (map_of_list el') k v
/-- 抽象映射函数:将具体二叉树投影为抽象偏映射 -/
def Abs {V : Type} (t : tree V) : partial_map V :=
map_of_list (elements t)
/-- 抽象偏映射查询操作 -/
def find {V : Type} (d : V) (k : key) (m : partial_map V) : V :=
match m k with
| some v => v
| none => d
/-- 抽象偏映射绑定状态判定 -/
def map_bound {V : Type} (k : key) (m : partial_map V) : Bool :=
match m k with
| some _ => true
| none => false
theorem empty_relate : ∀ (V : Type), @Abs V empty_tree = VFA.Maps.empty := by
intro V
rfl
/-- #### 练习:3 星,选做 (bound_relate) -/
theorem bound_relate : ∀ (V : Type) (t : tree V) (k : key),
BST t → map_bound k (Abs t) = bound k t := by
sorry
/-- #### 练习:3 星,选做 (lookup_relate) -/
theorem lookup_relate : ∀ (V : Type) (t : tree V) (d : V) (k : key),
BST t → find d k (Abs t) = lookup d k t := by
sorry
/-- 将关联列表作为键值插入 -/
def kvs_insert {V : Type} (k : key) (v : V) (kvs : List (key × V)) : List (key × V) :=
match kvs with
| [] => [(k, v)]
| (k', v') :: kvs' =>
if k < k' then (k, v) :: kvs
else if k > k' then (k', v') :: kvs_insert k v kvs'
else (k, v) :: kvs'
/-- #### 练习:3 星,选做 (kvs_insert_elements) -/
theorem kvs_insert_elements : ∀ (V : Type) (t : tree V),
BST t →
∀ (k : key) (v : V),
elements (insert k v t) = kvs_insert k v (elements t) := by
sorry
/-- #### 练习:3 星,选做 (insert_relate) -/
theorem insert_relate : ∀ (V : Type) (t : tree V) (k : key) (v : V),
BST t → Abs (insert k v t) = VFA.Maps.update (Abs t) k v := by
sorry
theorem elements_relate : ∀ (V : Type) (t : tree V),
BST t → map_of_list (elements t) = Abs t := by
intro V t _h
rfl7. 备用抽象关系 (Alternative Abstraction Relation)
lean
/-- 两偏映射的不相交并集运算 -/
def union {X : Type} (m1 m2 : partial_map X) : partial_map X :=
fun k =>
match m1 k, m2 k with
| none, none => none
| none, some v => some v
| some v, none => some v
| some _, some _ => none
/-- 基于树结构的直接抽象转换 -/
def map_of_tree {V : Type} (t : tree V) : partial_map V :=
match t with
| tree.E => VFA.Maps.empty
| tree.T l k v r => VFA.Maps.update (union (map_of_tree l) (map_of_tree r)) k v
/-- 备用抽象函数 -/
def Abs' {V : Type} (t : tree V) : partial_map V :=
map_of_tree t
theorem empty_relate' : ∀ (V : Type), @Abs' V empty_tree = VFA.Maps.empty := by
intro V
rfl
/-- #### 练习:3 星,进阶,选做 (bound_relate') -/
theorem bound_relate' : ∀ (V : Type) (t : tree V) (k : key),
BST t → map_bound k (Abs' t) = bound k t := by
sorry
/-- #### 练习:3 星,进阶,选做 (lookup_relate') -/
theorem lookup_relate' : ∀ (V : Type) (d : V) (t : tree V) (k : key),
BST t → find d k (Abs' t) = lookup d k t := by
sorry
/-- #### 练习:4 星,进阶,选做 (insert_relate') -/
theorem insert_relate' : ∀ (V : Type) (k : key) (v : V) (t : tree V),
BST t → Abs' (insert k v t) = VFA.Maps.update (Abs' t) k v := by
sorry
/-- #### 练习:4 星,进阶,选做 (elements_relate') -/
theorem elements_relate' : ∀ (V : Type) (t : tree V),
BST t → map_of_list (elements t) = Abs' t := by
sorry
end VFA.SearchTree