194 lines
5.5 KiB
OCaml
194 lines
5.5 KiB
OCaml
(* Weight-balanced binary trees.
|
|
These are binary trees such that one child of a node has at most N times
|
|
as many elements as the other child. We take N=3. *)
|
|
|
|
type 'a t = Empty | Node of 'a t * 'a * 'a t * int
|
|
(* The type of trees containing elements of type ['a].
|
|
[Empty] is the empty tree (containing no elements). *)
|
|
|
|
type 'a contents = Nothing | Something of 'a
|
|
(* Used with the functions [modify] and [List.split], to represent
|
|
the presence or the absence of an element in a tree. *)
|
|
|
|
(* Compute the size (number of nodes and leaves) of a tree. *)
|
|
|
|
let size = function
|
|
Empty -> 1
|
|
| Node(_, _, _, s) -> s
|
|
|
|
(* Creates a new node with left son l, value x and right son r.
|
|
l and r must be balanced and size l / size r must be between 1/N and N.
|
|
Inline expansion of size for better speed. *)
|
|
|
|
let new l x r =
|
|
let sl = match l with Empty -> 0 | Node(_,_,_,s) -> s in
|
|
let sr = match r with Empty -> 0 | Node(_,_,_,s) -> s in
|
|
Node(l, x, r, sl + sr + 1)
|
|
|
|
(* Same as new, but performs rebalancing if necessary.
|
|
Assumes l and r balanced, and size l / size r "reasonable"
|
|
(between 1/N^2 and N^2 ???).
|
|
Inline expansion of new for better speed in the most frequent case
|
|
where no rebalancing is required. *)
|
|
|
|
let bal l x r =
|
|
let sl = match l with Empty -> 0 | Node(_,_,_,s) -> s in
|
|
let sr = match r with Empty -> 0 | Node(_,_,_,s) -> s in
|
|
if sl > 3 * sr then begin
|
|
match l with
|
|
Empty -> invalid_arg "Baltree.bal"
|
|
| Node(ll, lv, lr, _) ->
|
|
if size ll >= size lr then
|
|
new ll lv (new lr x r)
|
|
else begin
|
|
match lr with
|
|
Empty -> invalid_arg "Baltree.bal"
|
|
| Node(lrl, lrv, lrr, _)->
|
|
new (new ll lv lrl) lrv (new lrr x r)
|
|
end
|
|
end else if sr > 3 * sl then begin
|
|
match r with
|
|
Empty -> invalid_arg "Baltree.bal"
|
|
| Node(rl, rv, rr, _) ->
|
|
if size rr >= size rl then
|
|
new (new l x rl) rv rr
|
|
else begin
|
|
match rl with
|
|
Empty -> invalid_arg "Baltree.bal"
|
|
| Node(rll, rlv, rlr, _) ->
|
|
new (new l x rll) rlv (new rlr rv rr)
|
|
end
|
|
end else
|
|
Node(l, x, r, sl + sr + 1)
|
|
|
|
(* Same as bal, but rebalance regardless of the original ratio
|
|
size l / size r *)
|
|
|
|
let rec join l x r =
|
|
match bal l x r with
|
|
Empty -> invalid_arg "Baltree.join"
|
|
| Node(l', x', r', _) as t' ->
|
|
let sl = size l' and sr = size r' in
|
|
if sl > 3 * sr or sr > 3 * sl then join l' x' r' else t'
|
|
|
|
(* Merge two trees l and r into one.
|
|
All elements of l must precede the elements of r.
|
|
Assumes size l / size r between 1/N and N. *)
|
|
|
|
let rec merge t1 t2 =
|
|
match (t1, t2) with
|
|
(Empty, t) -> t
|
|
| (t, Empty) -> t
|
|
| (Node(l1, v1, r1, h1), Node(l2, v2, r2, h2)) ->
|
|
bal l1 v1 (bal (merge r1 l2) v2 r2)
|
|
|
|
(* Same as merge, but does not assume anything about l and r. *)
|
|
|
|
let rec concat t1 t2 =
|
|
match (t1, t2) with
|
|
(Empty, t) -> t
|
|
| (t, Empty) -> t
|
|
| (Node(l1, v1, r1, h1), Node(l2, v2, r2, h2)) ->
|
|
join l1 v1 (join (concat r1 l2) v2 r2)
|
|
|
|
(* Insertion *)
|
|
|
|
let add searchpred x t =
|
|
let rec add = function
|
|
Empty ->
|
|
Node(Empty, x, Empty, 1)
|
|
| Node(l, v, r, _) as t ->
|
|
let c = searchpred v in
|
|
if c == 0 then t else
|
|
if c < 0 then bal (add l) v r else bal l v (add r)
|
|
in add t
|
|
|
|
(* Membership *)
|
|
|
|
let contains searchpred t =
|
|
let rec contains = function
|
|
Empty -> false
|
|
| Node(l, v, r, _) ->
|
|
let c = searchpred v in
|
|
if c == 0 then true else
|
|
if c < 0 then contains l else contains r
|
|
in contains t
|
|
|
|
(* Search *)
|
|
|
|
let find searchpred t =
|
|
let rec find = function
|
|
Empty ->
|
|
raise Not_found
|
|
| Node(l, v, r, _) ->
|
|
let c = searchpred v in
|
|
if c == 0 then v else
|
|
if c < 0 then find l else find r
|
|
in find t
|
|
|
|
(* Deletion *)
|
|
|
|
let remove searchpred t =
|
|
let rec remove = function
|
|
Empty ->
|
|
Empty
|
|
| Node(l, v, r, _) ->
|
|
let c = searchpred v in
|
|
if c == 0 then merge l r else
|
|
if c < 0 then bal (remove l) v r else bal l v (remove r)
|
|
in remove t
|
|
|
|
(* Modification *)
|
|
|
|
let modify searchpred modifier t =
|
|
let rec modify = function
|
|
Empty ->
|
|
begin match modifier Nothing with
|
|
Nothing -> Empty
|
|
| Something v -> Node(Empty, v, Empty, 1)
|
|
end
|
|
| Node(l, v, r, s) ->
|
|
let c = searchpred v in
|
|
if c == 0 then
|
|
begin match modifier(Something v) with
|
|
Nothing -> merge l r
|
|
| Something v' -> Node(l, v', r, s)
|
|
end
|
|
else if c < 0 then bal (modify l) v r else bal l v (modify r)
|
|
in modify t
|
|
|
|
(* Splitting *)
|
|
|
|
let split searchpred =
|
|
let rec split = function
|
|
Empty ->
|
|
(Empty, Nothing, Empty)
|
|
| Node(l, v, r, _) ->
|
|
let c = searchpred v in
|
|
if c == 0 then (l, Something v, r)
|
|
else if c < 0 then
|
|
let (ll, vl, rl) = split l in (ll, vl, join rl v r)
|
|
else
|
|
let (lr, vr, rr) = split r in (join l v lr, vr, rr)
|
|
in split
|
|
|
|
(* Comparison (by lexicographic ordering of the fringes of the two trees). *)
|
|
|
|
let compare cmp s1 s2 =
|
|
let rec compare_aux l1 l2 =
|
|
match (l1, l2) with
|
|
([], []) -> 0
|
|
| ([], _) -> -1
|
|
| (_, []) -> 1
|
|
| (Empty::t1, Empty::t2) ->
|
|
compare_aux t1 t2
|
|
| (Node(Empty, v1, r1, _) :: t1, Node(Empty, v2, r2, _) :: t2) ->
|
|
let c = cmp v1 v2 in
|
|
if c != 0 then c else compare_aux (r1::t1) (r2::t2)
|
|
| (Node(l1, v1, r1, _) :: t1, t2) ->
|
|
compare_aux (l1 :: Node(Empty, v1, r1, 0) :: t1) t2
|
|
| (t1, Node(l2, v2, r2, _) :: t2) ->
|
|
compare_aux t1 (l2 :: Node(Empty, v2, r2, 0) :: t2)
|
|
in
|
|
compare_aux [s1] [s2]
|