├── .gitignore ├── CHANGES.md ├── LICENSE ├── README.md ├── dune ├── dune-project ├── makefile ├── src ├── array_like.ml ├── circular.ml ├── dune ├── grow.ml ├── root.ml ├── varray.ml ├── varray.mli └── varray_sig.ml ├── tests ├── array_like_test.ml ├── bench_access.ml ├── dune └── exhaust.ml └── varray.opam /.gitignore: -------------------------------------------------------------------------------- 1 | *.annot 2 | *.cmo 3 | *.cma 4 | *.cmi 5 | *.a 6 | *.o 7 | *.cmx 8 | *.cmxs 9 | *.cmxa 10 | 11 | .merlin 12 | *.install 13 | *.coverage 14 | *.sw[lmnop] 15 | 16 | _build/ 17 | _doc/ 18 | _coverage/ 19 | _opam/ 20 | 21 | odoc.css 22 | bench.tsv 23 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | ## 0.1 (2022-04-09) 2 | 3 | Initial release. 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Arthur Wendling 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > **[Tiered Vectors: Efficient Dynamic Arrays for Rank-Based Sequences]** \[243ko pdf\]\ 2 | > by Michael T. Goodrich and John G. Kloss II \ 3 | > WADS 1999. Lecture Notes in Computer Science, vol 1663 https://doi.org/10.1007/3-540-48447-7\_21 4 | 5 | This library provides an implementation of **var**iable sized **arrays**, which 6 | are also called resizable arrays, dynamic arrays or even "vectors" in C++ and 7 | "ArrayList" in Java. Just like an array, accessing any element by its index is 8 | constant time, but one can also efficiently insert and delete at any location 9 | (with the array resizing automatically to meet the need). 10 | 11 | **[Online Documentation]** 12 | 13 | Following the above paper, the family of tiered vectors yields a nice 14 | compromise between random access and resizing: 15 | 16 | | Module Circular | `get`, `set` | `{push,pop}_{back,front}` | `insert_at`, `pop_at` | Memory overhead | 17 | |-------------------------------|-------------:|:-------------------------:|:----------------------------------:|:-----------------------:| 18 | | Circular | O(1) | O(1) amortized | O(N) | O(N) | 19 | | Root(Circular) | O(1) | O(1) amortized | O(√N) | O(√N) | 20 | | Rootk-1(Circular) | O(k) | O(k) amortized | O(k2 × k√N) | O(Nk-1 / k) | 21 | 22 | In other words, each instantiation of the `Root` functor leads to slower random 23 | access into the array, but it also makes insertion and deletion faster! 24 | 25 |  26 | 27 | You can expect the following constant factors on random access: 28 | 29 | | | Array | Circular | Root | Root2 | Root3 | Root4 | Root5 | 30 | |----:|------:|---------:|-----:|-----------------:|-----------------:|-----------------:|-----------------:| 31 | | get | 1x | 3x | 8x | 17x | 27x | 31x | 33x | 32 | | set | 1x | 2x | 4x | 8x | 12x | 14x | 15x | 33 | 34 | The memory usage is competitive: 35 | 36 | - `push_front`, `push_back` and their respective `pop`, are *amortized* 37 | constant time, since they frequently need to allocate small chunks of 38 | O(k√N) up to O(k k√N) memory as the varray grows or 39 | shrinks. 40 | - The growth strategy is incremental: the worst case slowdown following a 41 | resize is also O(k k√N) which is unobtrusive for k>1. There is no 42 | "stop the world while every elements is moved to a larger array". 43 | - The amount of memory used for bookkeeping and allocated in anticipation of a 44 | growth is pretty tight. In particular for k=2, the O(√N) memory overhead is 45 | optimal if random access and `push_back` are to be O(1). 46 | 47 | If you only care about fast random access and resizing at the right end with 48 | `{push,pop}_back`, then the pre-existing libraries provide smaller constant 49 | factors : (in alphabetical order) [BatDynArray] from Batteries, [CCVector] from 50 | Containers, [RES] as a standalone library or even [vector] as a single module. 51 | 52 | [Tiered Vectors: Efficient Dynamic Arrays for Rank-Based Sequences]: https://www.ics.uci.edu/~goodrich/pubs/wads99.pdf 53 | [Online Documentation]: https://art-w.github.io/varray/varray 54 | [BatDynArray]: https://ocaml-batteries-team.github.io/batteries-included/hdoc2/BatDynArray.html 55 | [CCVector]: https://c-cube.github.io/ocaml-containers/last/containers/CCVector/index.html 56 | [RES]: https://github.com/mmottl/res 57 | [vector]: https://github.com/backtracking/vector 58 | -------------------------------------------------------------------------------- /dune: -------------------------------------------------------------------------------- 1 | (dirs src tests) 2 | 3 | (env 4 | (release 5 | (flags (:standard -noassert)))) 6 | -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 2.8) 2 | (generate_opam_files true) 3 | 4 | (name varray) 5 | (source (github art-w/varray)) 6 | (license MIT) 7 | (authors "Arthur Wendling") 8 | (maintainers "art.wendling@gmail.com") 9 | (version 0.2) 10 | 11 | (package 12 | (name varray) 13 | (synopsis "Resizable arrays with fast insertion/deletion") 14 | (depends 15 | (ocaml (>= "4.08")) 16 | (monolith :with-test)) 17 | (description 18 | " 19 | - O(1) constant time for random access `arr.(i)` and updates `arr.(i) <- v` 20 | - O(1) amortized for `push_front` and `pop_front`, `push_back` and `pop_back` to add or remove an element to the start or the end 21 | - O(sqrt N) for `insert_at arr i x` and `delete_at arr i` to insert or delete an element anywhere else 22 | 23 | This datastructure was invented by Goodrich and Kloss and is described in their paper \"Tiered Vectors: Efficient Dynamic Arrays for Rank-Based Sequences\".") 24 | ) 25 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test 2 | test: 3 | dune exec --force tests/array_like_test.exe 4 | 5 | .PHONY: bench_access 6 | bench_access: 7 | dune exec --force --profile=release tests/bench_access.exe 8 | 9 | .PHONY: cover 10 | cover: clean 11 | dune exec --force --instrument-with bisect_ppx tests/array_like_test.exe 12 | bisect-ppx-report html 13 | bisect-ppx-report summary 14 | 15 | .PHONY: doc 16 | doc: odoc.css 17 | rm -rf _doc 18 | dune build @doc 19 | cp -r _build/default/_doc _doc 20 | [ -f odoc.css ] && cp -f odoc.css _doc/_html/odoc.css 21 | 22 | .PHONY: clean 23 | clean: 24 | dune clean 25 | rm -rf _doc 26 | rm -rf _coverage 27 | rm -f bisect*.coverage 28 | 29 | .PHONY: width80 30 | width80: 31 | find . -name '*.ml*' \ 32 | | grep -e '^./src' -e '^./tests' \ 33 | | grep -e '\.mli\?$$' \ 34 | | xargs grep --color -E -e '^.{80,}| $$' \ 35 | || echo 'OK' 36 | 37 | .PHONY: print 38 | print: 39 | find . -name '*.ml' \ 40 | | grep -e '^./src' \ 41 | | xargs grep --color -e 'print' \ 42 | || echo 'OK' 43 | -------------------------------------------------------------------------------- /src/array_like.ml: -------------------------------------------------------------------------------- 1 | module Make (Arg : Varray_sig.VARRAY) 2 | : Varray_sig.S with type 'a elt = 'a Arg.elt and type 'a array = 'a Arg.array 3 | = struct 4 | 5 | include Arg 6 | 7 | let sub t pos len = 8 | if pos < 0 || len < 0 || pos + len > length t 9 | then invalid_arg "Varray.sub" ; 10 | init len (fun i -> get t (pos + i)) 11 | 12 | let copy t = sub t 0 (length t) 13 | 14 | let fill t i n x = 15 | if i < 0 || n < 0 || i + n >= length t 16 | then invalid_arg "Varray.fill" ; 17 | for j = i to i + n - 1 do 18 | set t j x 19 | done 20 | 21 | let blit src src_pos dst dst_pos len = 22 | if src = dst && src_pos < dst_pos 23 | then for j = len - 1 downto 0 do 24 | set dst (j + dst_pos) (get src (j + src_pos)) 25 | done 26 | else for j = 0 to len - 1 do 27 | set dst (j + dst_pos) (get src (j + src_pos)) 28 | done 29 | 30 | let append a b = 31 | match length a, length b with 32 | | 0, _ -> copy b 33 | | _, 0 -> copy a 34 | | a_len, b_len -> 35 | let x = get a 0 in 36 | let t = make (a_len + b_len) x in 37 | blit a 1 t 1 (a_len - 1) ; 38 | blit b 0 t a_len b_len ; 39 | t 40 | 41 | let rec concat = function 42 | | [] -> empty () 43 | | t :: ts when is_empty t -> concat ts 44 | | (t :: _) as lst -> 45 | let len = 46 | List.fold_left (fun acc t -> acc + length t) 0 lst 47 | in 48 | let x = get t 0 in 49 | let result = make len x in 50 | let _ = 51 | List.fold_left 52 | (fun acc t -> 53 | let n = length t in 54 | blit t 0 result acc n ; 55 | acc + n) 56 | 0 57 | lst 58 | in 59 | result 60 | 61 | let iter f t = 62 | protect t @@ fun () -> 63 | for i = 0 to length t - 1 do 64 | f (get t i) 65 | done 66 | 67 | let iteri f t = 68 | protect t @@ fun () -> 69 | for i = 0 to length t - 1 do 70 | f i (get t i) 71 | done 72 | 73 | let map f t = match length t with 74 | | 0 -> empty () 75 | | n -> 76 | let x = f (get t 0) in 77 | let r = make n x in 78 | for i = 1 to n - 1 do 79 | set r i (f (get t i)) 80 | done ; 81 | r 82 | 83 | let map f t = protect t (fun () -> map f t) 84 | 85 | let mapi f t = match length t with 86 | | 0 -> empty () 87 | | n -> 88 | let x = f 0 (get t 0) in 89 | let r = make n x in 90 | for i = 1 to n - 1 do 91 | set r i (f i (get t i)) 92 | done ; 93 | r 94 | 95 | let mapi f t = protect t (fun () -> mapi f t) 96 | 97 | let fold_left f z t = 98 | let acc = ref z in 99 | for i = 0 to length t - 1 do 100 | acc := f !acc (get t i) 101 | done ; 102 | !acc 103 | 104 | let fold_left f z t = protect t (fun () -> fold_left f z t) 105 | 106 | let fold_right f t z = 107 | let acc = ref z in 108 | for i = length t - 1 downto 0 do 109 | acc := f (get t i) !acc 110 | done ; 111 | !acc 112 | 113 | let fold_right f t z = protect t (fun () -> fold_right f t z) 114 | 115 | let fold_left_map f z t = match length t with 116 | | 0 -> z, empty () 117 | | n -> 118 | let z, x = f z (get t 0) in 119 | let r = make n x in 120 | let acc = ref z in 121 | for i = 1 to n - 1 do 122 | let z, x = f !acc (get t i) in 123 | acc := z ; 124 | set r i x 125 | done ; 126 | !acc, r 127 | 128 | let fold_left_map f z t = protect t (fun () -> fold_left_map f z t) 129 | 130 | let iter2 f xs ys = 131 | let n, ys_len = length xs, length ys in 132 | if n <> ys_len then invalid_arg "Varray.iter2" ; 133 | for i = 0 to n - 1 do 134 | f (get xs i) (get ys i) 135 | done 136 | 137 | let iter2 f xs ys = 138 | protect xs (fun () -> protect ys (fun () -> iter2 f xs ys)) 139 | 140 | let map2 f xs ys = 141 | let n, ys_len = length xs, length ys in 142 | if n <> ys_len 143 | then invalid_arg "Varray.map2" 144 | else if n = 0 145 | then empty () 146 | else begin 147 | let x = f (get xs 0) (get ys 0) in 148 | let t = make n x in 149 | for i = 1 to n - 1 do 150 | let x = f (get xs i) (get ys i) in 151 | set t i x 152 | done ; 153 | t 154 | end 155 | 156 | let map2 f xs ys = 157 | protect xs (fun () -> protect ys (fun () -> map2 f xs ys)) 158 | 159 | exception Abort 160 | 161 | let for_all f t = 162 | try iter (fun x -> if not (f x) then raise Abort) t ; 163 | true 164 | with Abort -> false 165 | 166 | let for_all2 f xs ys = 167 | try iter2 (fun x y -> if not (f x y) then raise Abort) xs ys ; 168 | true 169 | with Abort -> false 170 | 171 | let exists f t = 172 | try iter (fun x -> if f x then raise Abort) t ; 173 | false 174 | with Abort -> true 175 | 176 | let exists2 f xs ys = 177 | try iter2 (fun x y -> if f x y then raise Abort) xs ys ; 178 | false 179 | with Abort -> true 180 | 181 | let mem x t = exists (( = ) x) t 182 | let memq x t = exists (( == ) x) t 183 | 184 | let find_opt (type a) f t = 185 | let exception Found of a elt in 186 | try iter (fun x -> if f x then raise (Found x)) t ; 187 | None 188 | with Found x -> Some x 189 | 190 | let find_map (type a) f t = 191 | let exception Found of a in 192 | let search x = match f x with 193 | | None -> () 194 | | Some y -> raise (Found y) 195 | in 196 | try iter search t ; 197 | None 198 | with Found x -> Some x 199 | 200 | let to_std_array t = Stdlib.Array.init (length t) (get t) 201 | 202 | let sort cmp t = 203 | let arr = to_std_array t in 204 | Stdlib.Array.sort cmp arr ; 205 | Stdlib.Array.iteri (set t) arr 206 | 207 | let stable_sort cmp t = 208 | let arr = to_std_array t in 209 | Stdlib.Array.stable_sort cmp arr ; 210 | Stdlib.Array.iteri (set t) arr 211 | 212 | let fast_sort cmp t = 213 | let arr = to_std_array t in 214 | Stdlib.Array.fast_sort cmp arr ; 215 | Stdlib.Array.iteri (set t) arr 216 | 217 | let of_array arr = 218 | init (Tier.Array.length arr) (Tier.Array.get arr) 219 | 220 | let to_array t = 221 | let n = length t in 222 | let arr = Arg.Tier.Array.create n in 223 | for i = 0 to n - 1 do 224 | Tier.Array.set arr i (get t i) 225 | done ; 226 | arr 227 | 228 | let to_list t = fold_right (fun x xs -> x :: xs) t [] 229 | 230 | let of_list = function 231 | | [] -> empty () 232 | | (x :: _) as lst -> 233 | let len = List.length lst in 234 | let t = make len x in 235 | List.iteri (fun i x -> set t i x) lst ; 236 | t 237 | 238 | let blit src src_pos dst dst_pos len = 239 | if src_pos < 0 || dst_pos < 0 || len < 0 240 | || src_pos + len > length src 241 | || dst_pos + len > length dst 242 | then invalid_arg "Varray.blit" ; 243 | blit src src_pos dst dst_pos len 244 | 245 | let pop_front t = 246 | if is_empty t then raise Not_found ; 247 | pop_front t 248 | 249 | let pop_back t = 250 | if is_empty t then raise Not_found ; 251 | pop_back t 252 | 253 | let pop_at t i = 254 | if i < 0 || i >= length t 255 | then invalid_arg "Varray.pop_at: index out of bounds" ; 256 | pop_at t i 257 | 258 | let delete_at t i = 259 | if i < 0 || i >= length t 260 | then invalid_arg "Varray.delete_at: index out of bounds" ; 261 | delete_at t i 262 | 263 | let insert_at t i = 264 | if i < 0 || i > length t 265 | then invalid_arg "Varray.insert_at: index out of bounds" ; 266 | insert_at t i 267 | 268 | let make n x = 269 | if n < 0 then invalid_arg "Varray.make" ; 270 | make n x 271 | 272 | let init n f = 273 | if n < 0 then invalid_arg "Varray.init" ; 274 | init n f 275 | 276 | end 277 | -------------------------------------------------------------------------------- /src/circular.ml: -------------------------------------------------------------------------------- 1 | let pow2 x = 1 lsl x 2 | 3 | module Make (Arg : Varray_sig.ARRAY) 4 | : sig 5 | include Varray_sig.TIER with type 'a Array.t = 'a Arg.t 6 | and type 'a Array.elt = 'a Arg.elt 7 | 8 | val set_length : 'a t -> int -> unit 9 | val grow_head : lc:int -> 'a t -> unit 10 | val grow_tail : 'a t -> unit 11 | val unsafe_pop_back : lc:int -> 'a t -> unit 12 | val root_capacity : 'a t -> int 13 | end 14 | = struct 15 | 16 | module Array = Arg 17 | type 'a elt = 'a Array.elt 18 | type 'a array = 'a Array.t 19 | 20 | type 'a t = 21 | { mutable head: int 22 | ; mutable length: int 23 | ; buffer: 'a Array.t 24 | } 25 | 26 | let depth = 1 27 | 28 | let length t = t.length 29 | 30 | let is_empty t = t.length = 0 31 | 32 | let capacity ~lc = pow2 lc 33 | 34 | let root_capacity t = Array.length t.buffer 35 | 36 | let is_full ~lc t = t.length = capacity ~lc 37 | 38 | let set_length t len = 39 | assert (len >= 0) ; 40 | t.length <- len 41 | 42 | let empty () = 43 | { head = 0 44 | ; length = 0 45 | ; buffer = Array.empty () 46 | } 47 | 48 | let create ~capacity = 49 | { head = 0 50 | ; length = 0 51 | ; buffer = Array.create capacity 52 | } 53 | 54 | let make ~lc n x = 55 | let buffer = Array.create (capacity ~lc) in 56 | for i = 0 to n - 1 do 57 | Array.set buffer i x 58 | done ; 59 | { head = 0 60 | ; length = n 61 | ; buffer 62 | } 63 | 64 | let init ~lc ~offset n f = 65 | let buffer = Array.create (capacity ~lc) in 66 | for i = 0 to n - 1 do 67 | let x = f (i + offset) in 68 | Array.set buffer i x 69 | done ; 70 | { head = 0 71 | ; length = n 72 | ; buffer 73 | } 74 | 75 | let index ~lc t i = (t.head + i) land (capacity ~lc - 1) 76 | let index_last ~lc t = index ~lc t (t.length - 1) 77 | 78 | let get ~lc t i = 79 | assert (i >= 0 && i < t.length) ; 80 | t.buffer.(index ~lc t i) 81 | 82 | let set ~lc t i x = 83 | assert (i >= 0 && i < t.length) ; 84 | t.buffer.(index ~lc t i) <- x 85 | 86 | let shift_right ~lc t j = 87 | let tail = index ~lc t t.length in 88 | if j <= tail 89 | then Array.blit t.buffer j t.buffer (j + 1) (tail - j) 90 | else begin 91 | let cap = capacity ~lc - 1 in 92 | let last = t.buffer.(cap) in 93 | Array.blit t.buffer j t.buffer (j + 1) (cap - j) ; 94 | Array.blit t.buffer 0 t.buffer 1 tail ; 95 | t.buffer.(0) <- last 96 | end 97 | 98 | let shift_left ~lc t j = 99 | let head = t.head in 100 | let cap = capacity ~lc in 101 | if j >= head 102 | then begin 103 | let prev = (head - 1) land (cap - 1) in 104 | t.buffer.(prev) <- t.buffer.(head) ; 105 | Array.blit t.buffer (head + 1) t.buffer head (j - head) 106 | end 107 | else begin 108 | Array.blit t.buffer head t.buffer (head - 1) (cap - head) ; 109 | t.buffer.(cap - 1) <- t.buffer.(0) ; 110 | Array.blit t.buffer 1 t.buffer 0 j ; 111 | end 112 | 113 | let head_left ~lc t = 114 | let head = index ~lc t (- 1) in 115 | t.head <- head 116 | 117 | let grow_tail t = 118 | t.length <- t.length + 1 119 | 120 | let grow_head ~lc t = 121 | assert (not (is_full ~lc t)) ; 122 | head_left ~lc t ; 123 | grow_tail t 124 | 125 | let push_front ~lc t x = 126 | assert (not (is_full ~lc t)) ; 127 | grow_head ~lc t ; 128 | t.buffer.(t.head) <- x 129 | 130 | let push_back ~lc t x = 131 | assert (not (is_full ~lc t)) ; 132 | grow_tail t ; 133 | t.buffer.(index_last ~lc t) <- x 134 | 135 | let make_room ~lc t i = 136 | assert (not (is_full ~lc t)) ; 137 | if 2 * i >= t.length 138 | then begin 139 | let j = index ~lc t i in 140 | shift_right ~lc t j ; 141 | grow_tail t 142 | end 143 | else begin 144 | let j = index ~lc t i in 145 | shift_left ~lc t j ; 146 | grow_head ~lc t 147 | end 148 | 149 | let insert_at ~lc t i x = 150 | assert (i >= 0 && i <= t.length) ; 151 | assert (not (is_full ~lc t)) ; 152 | make_room ~lc t i ; 153 | set ~lc t i x 154 | 155 | 156 | let shrink_tail t tail = 157 | assert (t.length > 0) ; 158 | Array.erase_at t.buffer tail ; 159 | t.length <- t.length - 1 160 | 161 | let shrink_head ~lc t head = 162 | assert (t.length > 0) ; 163 | assert (head = t.head) ; 164 | Array.erase_at t.buffer t.head ; 165 | t.head <- index ~lc t 1 ; 166 | t.length <- t.length - 1 167 | 168 | let shrink_next_tail ~lc t = 169 | let cap = capacity ~lc in 170 | if t.length + 1 < cap 171 | then let next = (t.head + t.length) land (cap - 1) in 172 | Array.erase_at t.buffer next 173 | 174 | let unsafe_pop_back ~lc t = 175 | shrink_next_tail ~lc t ; 176 | assert (t.length > 0) ; 177 | t.length <- t.length - 1 178 | 179 | let delete_right ~lc t j = 180 | let tail = index_last ~lc t in 181 | if j = tail 182 | then () 183 | else if j < tail 184 | then Array.blit t.buffer (j + 1) t.buffer j (tail - j) 185 | else begin 186 | let cap = capacity ~lc in 187 | Array.blit t.buffer (j + 1) t.buffer j (cap - 1 - j) ; 188 | t.buffer.(cap - 1) <- t.buffer.(0) ; 189 | Array.blit t.buffer 1 t.buffer 0 tail 190 | end ; 191 | shrink_tail t tail 192 | 193 | let delete_left ~lc t j = 194 | let head = t.head in 195 | if j = head 196 | then () 197 | else if head < j 198 | then Array.blit t.buffer head t.buffer (head + 1) (j - head) 199 | else begin 200 | let cap = capacity ~lc in 201 | let last = t.buffer.(cap - 1) in 202 | Array.blit t.buffer head t.buffer (head + 1) (cap - 1 - head) ; 203 | Array.blit t.buffer 0 t.buffer 1 j ; 204 | t.buffer.(0) <- last ; 205 | end ; 206 | shrink_head ~lc t head 207 | 208 | let delete_at ~lc t i = 209 | let j = index ~lc t i in 210 | if 2 * i >= t.length 211 | then delete_right ~lc t j 212 | else delete_left ~lc t j 213 | 214 | let pop_at ~lc t i = 215 | let x = get ~lc t i in 216 | delete_at ~lc t i ; 217 | x 218 | 219 | let pop_front ~lc t = 220 | assert (t.length > 0) ; 221 | let x = t.buffer.(t.head) in 222 | shrink_head ~lc t t.head ; 223 | x 224 | 225 | let pop_back ~lc t = 226 | assert (t.length > 0) ; 227 | let tail = index_last ~lc t in 228 | let x = t.buffer.(tail) in 229 | shrink_tail t tail ; 230 | x 231 | 232 | let push_front_pop_back ~lc t x = 233 | let tail = index_last ~lc t in 234 | let last = t.buffer.(tail) in 235 | head_left ~lc t ; 236 | t.buffer.(t.head) <- x ; 237 | last 238 | 239 | let push_back_pop_front ~lc t x = 240 | let first = t.buffer.(t.head) in 241 | t.head <- index ~lc t 1 ; 242 | let last = index_last ~lc t in 243 | t.buffer.(last) <- x ; 244 | first 245 | 246 | end 247 | -------------------------------------------------------------------------------- /src/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (public_name varray) 3 | (instrumentation (backend bisect_ppx))) 4 | -------------------------------------------------------------------------------- /src/grow.ml: -------------------------------------------------------------------------------- 1 | let ( /^ ) a b = 1 + (a - 1) / b 2 | 3 | let rec log2 = function 4 | | 0 | 1 -> 0 5 | | n -> 1 + log2 (n /^ 2) 6 | 7 | let pow2 n = 1 lsl n 8 | 9 | module Make (V : Varray_sig.TIER) 10 | : Varray_sig.VARRAY with type 'a elt = 'a V.elt and type 'a array = 'a V.array 11 | = struct 12 | 13 | module Tier = V 14 | 15 | module Array = V.Array 16 | type 'a elt = 'a V.elt 17 | type 'a array = 'a V.array 18 | 19 | type 'a t = 20 | { mutable lc : int 21 | ; mutable protected : bool 22 | ; mutable small : 'a V.t 23 | ; mutable large : 'a V.t 24 | } 25 | 26 | let length t = 27 | V.length t.small + V.length t.large 28 | 29 | let lc_for = function 30 | | n when n <= 0 -> 1 31 | | n -> log2 n /^ V.depth 32 | 33 | let empty () = 34 | { lc = 0 35 | ; protected = false 36 | ; small = V.create ~capacity:1 37 | ; large = V.empty () 38 | } 39 | 40 | let is_empty t = V.is_empty t.small && V.is_empty t.large 41 | 42 | let make n x = 43 | let lc = lc_for n in 44 | { lc 45 | ; protected = false 46 | ; small = V.make ~lc n x 47 | ; large = V.empty () 48 | } 49 | 50 | let init n f = 51 | let lc = lc_for n in 52 | { lc 53 | ; protected = false 54 | ; small = V.init ~lc ~offset:0 n f 55 | ; large = V.empty () 56 | } 57 | 58 | let protect t f = 59 | if t.protected 60 | then f () 61 | else begin 62 | t.protected <- true ; 63 | match f () with 64 | | v -> t.protected <- false ; v 65 | | exception e -> t.protected <- false ; raise e 66 | end 67 | 68 | let check_protection t = 69 | if t.protected 70 | then failwith "Varray modification during protected traversal" 71 | 72 | let get t i = 73 | let lc = t.lc in 74 | match i - V.length t.small with 75 | | j when i >= 0 && j < 0 -> 76 | V.get ~lc t.small i 77 | | j when j >= 0 && j < V.length t.large -> 78 | V.get ~lc:(lc + 1) t.large j 79 | | _ -> 80 | invalid_arg "index out of bounds" 81 | 82 | let set t i x = 83 | let lc = t.lc in 84 | match i - V.length t.small with 85 | | j when i >= 0 && j < 0 -> 86 | V.set ~lc t.small i x 87 | | j when j >= 0 && j < V.length t.large -> 88 | V.set ~lc:(lc + 1) t.large j x 89 | | _ -> 90 | invalid_arg "index out of bounds" 91 | 92 | let do_swap t = 93 | t.lc <- 1 + t.lc ; 94 | t.small <- t.large ; 95 | t.large <- V.empty () 96 | 97 | let swap t = 98 | if V.is_empty t.small && not (V.is_empty t.large) 99 | then do_swap t 100 | 101 | let is_growing t = length t >= V.capacity ~lc:t.lc 102 | 103 | let pow2_depth = pow2 V.depth 104 | 105 | let incr_capacity t = 106 | swap t ; 107 | if is_growing t 108 | then begin 109 | if not (V.is_empty t.large) 110 | then begin 111 | assert (not (V.is_empty t.small)) ; 112 | let lc = t.lc in 113 | let tl = V.pop_back ~lc t.small in 114 | let lc = t.lc + 1 in 115 | V.push_front ~lc t.large tl ; 116 | if V.is_empty t.small 117 | then do_swap t 118 | end 119 | else begin 120 | let tl = V.pop_back ~lc:t.lc t.small in 121 | let lc = 1 + t.lc in 122 | t.large <- V.make ~lc 1 tl 123 | end 124 | end 125 | 126 | let insert_at t i x = 127 | check_protection t ; 128 | incr_capacity t ; 129 | match i - V.length t.small with 130 | | j when j <= 0 -> 131 | V.insert_at ~lc:t.lc t.small i x ; 132 | incr_capacity t 133 | | j -> 134 | V.insert_at ~lc:(t.lc + 1) t.large j x 135 | 136 | let is_shrinking t = 137 | length t * pow2_depth < V.capacity ~lc:t.lc 138 | 139 | let decr_capacity t = 140 | swap t ; 141 | if is_shrinking t 142 | then begin 143 | if not (V.is_empty t.large) 144 | then begin 145 | let lc = t.lc in 146 | assert (not (V.is_full ~lc t.small)) ; 147 | V.push_back ~lc t.small (V.pop_front ~lc:(lc + 1) t.large) 148 | end 149 | else if t.lc > 1 && not (V.is_empty t.small) 150 | then begin 151 | let lc = t.lc in 152 | let hd = V.pop_front ~lc t.small in 153 | let lc = t.lc - 1 in 154 | t.lc <- lc ; 155 | t.large <- t.small ; 156 | t.small <- V.make ~lc 1 hd ; 157 | end 158 | end 159 | 160 | let pop_at t i = 161 | check_protection t ; 162 | decr_capacity t ; 163 | match i - V.length t.small with 164 | | j when j < 0 -> 165 | let x = V.pop_at ~lc:t.lc t.small i in 166 | decr_capacity t ; 167 | x 168 | | j -> 169 | V.pop_at ~lc:(t.lc + 1) t.large j 170 | 171 | let delete_at t i = ignore (pop_at t i) 172 | 173 | let push_back t x = 174 | check_protection t ; 175 | incr_capacity t ; 176 | let lc = t.lc in 177 | if V.is_empty t.large 178 | then V.push_back ~lc t.small x 179 | else V.push_back ~lc:(lc + 1) t.large x 180 | 181 | let push_front t x = 182 | check_protection t ; 183 | incr_capacity t ; 184 | let lc = t.lc in 185 | V.push_front ~lc t.small x ; 186 | incr_capacity t 187 | 188 | let pop_front t = 189 | check_protection t ; 190 | decr_capacity t ; 191 | let x = V.pop_front ~lc:t.lc t.small in 192 | decr_capacity t ; 193 | x 194 | 195 | let pop_back t = 196 | check_protection t ; 197 | decr_capacity t ; 198 | if V.is_empty t.large 199 | then V.pop_back ~lc:t.lc t.small 200 | else V.pop_back ~lc:(t.lc + 1) t.large 201 | 202 | end 203 | -------------------------------------------------------------------------------- /src/root.ml: -------------------------------------------------------------------------------- 1 | let pow2 x = 1 lsl x 2 | 3 | module Make (V : Varray_sig.TIER) 4 | : Varray_sig.TIER with module Array = V.Array 5 | = struct 6 | 7 | module Buffer = Circular.Make (struct 8 | include Array 9 | type 'a elt = 'a V.t 10 | type 'a t = 'a elt array 11 | let get = Array.unsafe_get 12 | let set = Array.unsafe_set 13 | let empty : type a. unit -> a t = fun () -> [| |] 14 | let create n = Array.make n (V.empty ()) 15 | let erase_at t i = set t i (V.empty ()) 16 | end) 17 | 18 | module Array = V.Array 19 | type 'a elt = 'a V.elt 20 | type 'a array = 'a V.array 21 | 22 | type 'a t = 23 | { mutable length : int 24 | ; mutable first : 'a V.t 25 | ; mutable rows : 'a Buffer.t 26 | } 27 | 28 | let empty () = 29 | { length = 0 ; first = V.empty () ; rows = Buffer.empty () } 30 | 31 | let is_empty t = t.length = 0 32 | 33 | let depth = V.depth + 1 34 | 35 | let sector_length ~lc = pow2 (lc * V.depth) 36 | 37 | let capacity ~lc = pow2 (lc * depth) 38 | 39 | let length t = t.length 40 | 41 | let is_full ~lc t = length t = capacity ~lc 42 | 43 | let root_capacity t = Buffer.root_capacity t.rows 44 | 45 | let create ~capacity = 46 | { length = 0 47 | ; first = V.empty () 48 | ; rows = Buffer.create ~capacity 49 | } 50 | 51 | let make ~lc n x = 52 | let capacity = pow2 lc in 53 | assert (capacity > 0) ; 54 | let sector_length = sector_length ~lc in 55 | let remaining, nb_full_parts = 56 | match n mod sector_length with 57 | | 0 when n < sector_length -> n, 0 58 | | 0 -> sector_length, n / sector_length - 1 59 | | rest -> rest, n / sector_length 60 | in 61 | let t = create ~capacity in 62 | t.length <- n ; 63 | Buffer.set_length t.rows nb_full_parts ; 64 | assert (remaining <= n) ; 65 | assert ((n > 0) = (remaining > 0)) ; 66 | assert (n = remaining + sector_length * nb_full_parts) ; 67 | t.first <- V.make ~lc remaining x ; 68 | for i = 0 to nb_full_parts - 1 do 69 | let row = V.make ~lc sector_length x in 70 | Buffer.set ~lc t.rows i row 71 | done ; 72 | t 73 | 74 | let init ~lc ~offset n f = 75 | let capacity = pow2 lc in 76 | assert (capacity > 0) ; 77 | let sector_length = sector_length ~lc in 78 | let remaining, nb_full_parts = 79 | match n mod sector_length with 80 | | 0 when n < sector_length -> n, 0 81 | | 0 -> sector_length, n / sector_length - 1 82 | | rest -> rest, n / sector_length 83 | in 84 | let t = create ~capacity in 85 | t.length <- n ; 86 | Buffer.set_length t.rows nb_full_parts ; 87 | assert (remaining <= n) ; 88 | assert ((n > 0) = (remaining > 0)) ; 89 | assert (n = remaining + sector_length * nb_full_parts) ; 90 | t.first <- V.init ~lc ~offset remaining f ; 91 | let offset = offset + remaining in 92 | for i = 0 to nb_full_parts - 1 do 93 | let offset = offset + i * sector_length in 94 | let row = V.init ~lc ~offset sector_length f in 95 | Buffer.set ~lc t.rows i row 96 | done ; 97 | t 98 | 99 | let has_capacity child = V.root_capacity child > 0 100 | 101 | let create_child ~lc t i x = 102 | let row = V.make ~lc 1 x in 103 | Buffer.set ~lc t.rows i row 104 | 105 | let initialize ~lc t = 106 | assert (Buffer.root_capacity t.rows = pow2 lc) 107 | 108 | let push_front_new ~lc t x = 109 | initialize ~lc t ; 110 | Buffer.grow_head ~lc t.rows ; 111 | let fst = Buffer.get ~lc t.rows 0 in 112 | assert (V.is_empty fst) ; 113 | assert (V.is_full ~lc t.first) ; 114 | Buffer.set ~lc t.rows 0 t.first ; 115 | t.first <- 116 | if has_capacity fst 117 | then (V.push_front ~lc fst x ; fst) 118 | else V.make ~lc 1 x 119 | 120 | let push_front ~lc t x = 121 | assert (not (is_full ~lc t)) ; 122 | begin 123 | if is_empty t 124 | then if has_capacity t.first 125 | then V.push_front ~lc t.first x 126 | else t.first <- V.make ~lc 1 x 127 | else if V.is_full ~lc t.first 128 | then push_front_new ~lc t x 129 | else V.push_front ~lc t.first x 130 | end ; 131 | t.length <- t.length + 1 ; 132 | assert (V.length t.first > 0) 133 | 134 | let push_back_new ~lc t x = 135 | initialize ~lc t ; 136 | let last_idx = Buffer.length t.rows in 137 | Buffer.grow_tail t.rows ; 138 | let fst = Buffer.get ~lc t.rows last_idx in 139 | assert (V.is_empty fst) ; 140 | if has_capacity fst 141 | then V.push_back ~lc fst x 142 | else create_child ~lc t last_idx x 143 | 144 | let push_back ~lc t x = 145 | assert (not (is_full ~lc t)) ; 146 | let n = Buffer.length t.rows - 1 in 147 | begin 148 | if n < 0 149 | then if not (has_capacity t.first) || V.is_full ~lc t.first 150 | then push_back_new ~lc t x 151 | else V.push_back ~lc t.first x 152 | else let tail = Buffer.get ~lc t.rows n in 153 | if V.is_full ~lc tail 154 | then push_back_new ~lc t x 155 | else V.push_back ~lc tail x 156 | end ; 157 | t.length <- t.length + 1 158 | 159 | let clean_front ~lc t = 160 | if V.is_empty t.first && Buffer.length t.rows > 0 161 | then t.first <- Buffer.pop_front ~lc t.rows 162 | 163 | let pop_front ~lc t = 164 | let first = t.first in 165 | let v = V.pop_front ~lc first in 166 | clean_front ~lc t ; 167 | t.length <- t.length - 1 ; 168 | v 169 | 170 | let clean_back ~lc t last = 171 | if V.is_empty last 172 | then begin 173 | assert (Buffer.length t.rows > 0) ; 174 | Buffer.unsafe_pop_back ~lc t.rows 175 | end 176 | 177 | let pop_back ~lc t = 178 | t.length <- t.length - 1 ; 179 | let i = Buffer.length t.rows - 1 in 180 | if i < 0 181 | then begin 182 | let x = V.pop_back ~lc t.first in 183 | clean_front ~lc t ; 184 | x 185 | end 186 | else begin 187 | let last = Buffer.get ~lc t.rows i in 188 | let v = V.pop_back ~lc last in 189 | clean_back ~lc t last ; 190 | v 191 | end 192 | 193 | let indexes' ~lc t i = 194 | let first = t.first in 195 | let first_len = V.length first in 196 | if i < first_len 197 | then 0, i 198 | else let i = i - first_len in 199 | let lcd = lc * V.depth in 200 | let j = 1 + i lsr lcd in 201 | let i = i land (pow2 lcd - 1) in 202 | j, i 203 | 204 | let indexes ~lc t i = 205 | if i = 0 206 | then 0, 0 207 | else indexes' ~lc t i 208 | 209 | let buffer_get ~lc t j = 210 | if j = 0 211 | then t.first 212 | else Buffer.get ~lc t.rows (j - 1) 213 | 214 | let get ~lc t i = 215 | assert (i >= 0 && i < length t) ; 216 | let j, i = indexes' ~lc t i in 217 | let row = buffer_get ~lc t j in 218 | V.get ~lc row i 219 | 220 | let set ~lc t i x = 221 | assert (i >= 0 && i < length t) ; 222 | let j, i = indexes' ~lc t i in 223 | let row = buffer_get ~lc t j in 224 | V.set ~lc row i x 225 | 226 | let collapse ~lc t j row = 227 | let len = Buffer.length t.rows in 228 | if 2 * j < len 229 | then begin 230 | let first = t.first in 231 | let v = ref (V.pop_back ~lc first) in 232 | for k = 0 to j - 2 do 233 | let row = Buffer.get ~lc t.rows k in 234 | v := V.push_front_pop_back ~lc row !v 235 | done ; 236 | V.push_front ~lc row !v ; 237 | clean_front ~lc t ; 238 | assert (V.length t.first > 0) ; 239 | for i = 0 to Buffer.length t.rows - 2 do 240 | let row = Buffer.get ~lc t.rows i in 241 | assert (V.length row = sector_length ~lc) 242 | done 243 | 244 | end 245 | else begin 246 | let len = len - 1 in 247 | let last = Buffer.get ~lc t.rows len in 248 | let v = ref (V.pop_front ~lc last) in 249 | for k = len - 1 downto j do 250 | let row = Buffer.get ~lc t.rows k in 251 | v := V.push_back_pop_front ~lc row !v ; 252 | done ; 253 | V.push_back ~lc row !v ; 254 | clean_back ~lc t last 255 | end 256 | 257 | let pop_at ~lc t i = 258 | assert (i >= 0 && i < length t) ; 259 | let j, i = indexes ~lc t i in 260 | let row = buffer_get ~lc t j in 261 | assert (j >= 0 && j <= Buffer.length t.rows) ; 262 | assert (i >= 0 && i < V.length row) ; 263 | let x = V.pop_at ~lc row i in 264 | t.length <- t.length - 1 ; 265 | if j = 0 266 | then clean_front ~lc t 267 | else if j >= Buffer.length t.rows 268 | then clean_back ~lc t row 269 | else collapse ~lc t j row ; 270 | x 271 | 272 | let push_front_pop_back ~lc t x = 273 | let y = pop_back ~lc t in 274 | push_front ~lc t x ; 275 | y 276 | 277 | let push_back_pop_front ~lc t x = 278 | let y = pop_front ~lc t in 279 | push_back ~lc t x ; 280 | y 281 | 282 | let insert_at ~lc t i x = 283 | assert (not (is_full ~lc t)) ; 284 | if i = 0 285 | then push_front ~lc t x 286 | else begin 287 | let j, i = indexes ~lc t i in 288 | let len = Buffer.length t.rows in 289 | if j = 0 290 | then begin 291 | t.length <- t.length + 1 ; 292 | if V.is_full ~lc t.first 293 | then begin 294 | assert (i > 0) ; 295 | let y = V.pop_front ~lc t.first in 296 | V.insert_at ~lc t.first (i - 1) x ; 297 | push_front_new ~lc t y 298 | end 299 | else V.insert_at ~lc t.first i x 300 | end 301 | else if j > len 302 | then push_back ~lc t x 303 | else begin 304 | let j = j - 1 in 305 | let row = Buffer.get ~lc t.rows j in 306 | if 2 * j < len 307 | then begin 308 | let v = 309 | if i = 0 310 | then x 311 | else begin 312 | let y = V.pop_front ~lc row in 313 | V.insert_at ~lc row (i - 1) x ; 314 | y 315 | end 316 | in 317 | let v = ref v in 318 | for k = j - 1 downto 0 do 319 | let row = Buffer.get ~lc t.rows k in 320 | v := V.push_back_pop_front ~lc row !v 321 | done ; 322 | v := V.push_back_pop_front ~lc t.first !v ; 323 | push_front ~lc t !v 324 | end 325 | else begin 326 | let v = 327 | if i = V.length row 328 | then x 329 | else begin 330 | let y = V.pop_back ~lc row in 331 | V.insert_at ~lc row i x ; 332 | y 333 | end 334 | in 335 | let v = ref v in 336 | for k = j + 1 to len - 1 do 337 | let row = Buffer.get ~lc t.rows k in 338 | v := V.push_front_pop_back ~lc row !v 339 | done ; 340 | push_back ~lc t !v 341 | end 342 | end 343 | end 344 | 345 | end 346 | -------------------------------------------------------------------------------- /src/varray.ml: -------------------------------------------------------------------------------- 1 | module Array_backend 2 | : Varray_sig.ARRAY with type 'a elt = 'a 3 | and type 'a t = 'a array 4 | = struct 5 | include Array 6 | type 'a elt = 'a 7 | let get = Array.unsafe_get 8 | let set = Array.unsafe_set 9 | let empty : type a. unit -> a t = fun () -> [| |] 10 | let placeholder : type a. a elt = Obj.magic () 11 | let create n = Array.make n placeholder 12 | let erase_at t i = set t i placeholder 13 | end 14 | 15 | module type ARRAY = Varray_sig.ARRAY 16 | 17 | module type ARRAY_TYPES = sig 18 | type 'a array_elt 19 | type 'a array_t 20 | end 21 | 22 | module Internals (X : ARRAY_TYPES) = struct 23 | module type UNSAFE = Varray_sig.TIER with type 'a Array.elt = 'a X.array_elt 24 | and type 'a Array.t = 'a X.array_t 25 | end 26 | 27 | module type S = sig 28 | include Varray_sig.S 29 | module Backend : sig 30 | type 'a array_elt = 'a elt 31 | type 'a array_t = 'a array 32 | end 33 | module Unsafe : Internals(Backend).UNSAFE 34 | end 35 | 36 | module Grow (Arg : Varray_sig.TIER) 37 | : S with type 'a elt = 'a Arg.elt and type 'a array = 'a Arg.array 38 | = struct 39 | module V = Grow.Make (Arg) 40 | include Array_like.Make (V) 41 | module Backend = struct 42 | type 'a array_elt = 'a elt 43 | type 'a array_t = 'a array 44 | end 45 | module Unsafe : Internals(Backend).UNSAFE = V.Tier 46 | end 47 | 48 | module Make (Array : ARRAY) 49 | : S with type 'a array = 'a Array.t and type 'a elt = 'a Array.elt 50 | = Grow (Circular.Make (Array)) 51 | 52 | module Root (V : S) 53 | : S with type 'a array = 'a V.array and type 'a elt = 'a V.elt 54 | = Grow (Root.Make (V.Unsafe)) 55 | 56 | module Circular 57 | : S with type 'a array = 'a Stdlib.Array.t and type 'a elt = 'a 58 | = Make (Array_backend) 59 | 60 | include Root (Circular) 61 | -------------------------------------------------------------------------------- /src/varray.mli: -------------------------------------------------------------------------------- 1 | (** 2 | A varray is a {b var}iable sized {b array}, also known as a resizable or 3 | dynamic array. 4 | 5 | Just like an array, random access / update is {b O(1)}. But you can also grow 6 | the varray by appending or prepending an element in constant time. 7 | Insertion and deletion at a specific index cost {b O({%html:k%}√N)} 8 | for any constant [k ≥ 1] of your choosing. 9 | 10 | For convenience, the recommended complexity tradeoff between time and memory is 11 | provided below with the constant parameter [k = 2]. You will find the internal 12 | building blocks at the end of this documentation to create a custom Varray with 13 | different asymptotics. 14 | *) 15 | 16 | include Varray_sig.S with type 'a elt = 'a 17 | and type 'a array = 'a Stdlib.Array.t (** @inline *) 18 | 19 | (** {1 Signature} *) 20 | 21 | module Internals (X : sig type 'a array_elt type 'a array_t end) : sig 22 | module type UNSAFE 23 | end 24 | (** The signature of the internal operations, required by the {! Root} functor 25 | below. *) 26 | 27 | module type S = sig 28 | include Varray_sig.S (** @inline *) 29 | 30 | (** {1 Internals} *) 31 | 32 | (** This part can be ignored as it exposes no user-facing functionality!.. 33 | but the design pattern is neat. The {! Root} functor requires access to 34 | internal operations, that should neither be called nor implemented by a 35 | user of this library. 36 | *) 37 | 38 | module Backend : sig 39 | type 'a array_elt = 'a elt 40 | type 'a array_t = 'a array 41 | end 42 | (** The ['a array] and ['a elt] types. *) 43 | 44 | module Unsafe : Internals(Backend).UNSAFE 45 | (** The internal operations, safely concealed behind an abstract signature! 46 | *) 47 | 48 | (** 49 | This could not have been written as: 50 | 51 | {[ module Unsafe : UNSAFE with type 'a array = 'a array 52 | and type 'a elt = 'a elt ]} 53 | 54 | Since the signature [UNSAFE] can't be type constrained without also 55 | having all its internal functions be public. The {! Internals} functor 56 | circumvent this rule by exposing an opaque signature parametrized by 57 | the type constraints. *) 58 | end 59 | (** The signature of a varray. *) 60 | 61 | (** {1 Build your own} *) 62 | 63 | (** The family of varrays is defined by calling the {! Root} functor as many 64 | times as required: *) 65 | 66 | (** 67 | 68 | {%html: 69 | 79 |
Module | 83 |get , set
84 | 85 | push , pop |
86 | insert_at delete_at |
87 | Memory overhead | 88 |
---|---|---|---|
Circular |
93 | O(1) | 94 |O(N) | 95 |O(N) | 96 |
Root (Circular) |
99 | O(1) | 100 |O(2√N) | 101 |O(2√N) | 102 |
Root (Root (Circular)) |
105 | O(1) | 106 |O(3√N) | 107 |O(N2/3) | 108 |
Rootk-1 (Circular) |
111 | O(k) | 112 |O(k2 × k√N) | 113 |O(Nk-1 / k) | 114 |