├── .gitignore ├── test ├── README.markdown ├── skewbinheap_eqc.erl └── skewbinheap.erl /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | ~* 3 | *.beam 4 | erl_crash.dump 5 | current_counterexample.eqc 6 | -------------------------------------------------------------------------------- /test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | 3 | test(N) -> 4 | skewbinheap_eqc:test_min(N) and 5 | skewbinheap_eqc:test_sort(N) and 6 | skewbinheap_eqc:test_merge(N). 7 | 8 | main([String]) -> 9 | N = try 10 | list_to_integer(String) 11 | catch 12 | _:_ -> usage() 13 | end, 14 | 15 | compile:file(skewbinheap), 16 | compile:file(skewbinheap_eqc), 17 | 18 | test(N); 19 | main(_) -> main(["100"]). 20 | 21 | usage() -> 22 | io:format("usage: test [iterations]\n"), 23 | halt(1). 24 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Skew Binomial Heaps 2 | == 3 | 4 | Skew Binomial Heaps offer amortized: 5 | 6 | O(1) insertion 7 | O(1) merge 8 | O(1) lowest element 9 | O(log(N)) removing of lowest element 10 | 11 | This implementation is straight from Okasaki's PFDS. 12 | 13 | Example 14 | -- 15 | 16 | > Heap = skewbinheap:new(). 17 | [] 18 | 19 | > H1 = skewbinheap:insert_all([2,1,4,5], Heap). 20 | [{0,5,[],[]},{1,1,[4],[{0,2,[],[]}]}] 21 | 22 | > skewbinheap:min(H1) 23 | 1 24 | 25 | > skewbinheap:delete_min(H1). 26 | [{0,4,[],[]},{1,2,[],[{0,5,[],[]}]}] 27 | 28 | > H2 = skewbinheap:insert_all([100,101], skewbinheap:new()). 29 | [{0,101,[],[]},{0,100,[],[]}] 30 | 31 | > skewbinheap:merge(H1, H2) 32 | [{0,5,[],[]},{2,1,[4],[{1,100,[],[{0,101,[],[]}]},{0,2,[],[]}]}] 33 | TODO 34 | -- 35 | 36 | - Add a custom comparator function 37 | -------------------------------------------------------------------------------- /skewbinheap_eqc.erl: -------------------------------------------------------------------------------- 1 | -module(skewbinheap_eqc). 2 | 3 | -include_lib("eqc/include/eqc.hrl"). 4 | 5 | -compile(export_all). 6 | 7 | % Deconstruct a heap in sorted order. n log n. :( 8 | ary([]) -> []; 9 | ary(Heap) -> 10 | {X, Heap1} = skewbinheap:find_delete_min(Heap), 11 | [X | ary(Heap1)]. 12 | 13 | % Test that inserting elements returns the least, or throws empty. 14 | prop_min() -> 15 | ?FORALL({Xs}, {list(int())}, 16 | begin 17 | H = skewbinheap:insert_all(Xs, skewbinheap:new()), 18 | try 19 | equals(skewbinheap:min(H), lists:min(Xs)) 20 | catch 21 | throw:empty -> 22 | length(Xs) == 0 23 | end 24 | end). 25 | 26 | test_min(N) -> quickcheck(numtests(N, prop_min())). 27 | 28 | % Test that successive minimum elements are sorted. 29 | prop_sort() -> 30 | ?FORALL({Xs}, {list(int())}, 31 | begin 32 | H = skewbinheap:insert_all(Xs, skewbinheap:new()), 33 | equals(ary(H), lists:sort(Xs)) 34 | end). 35 | 36 | test_sort(N) -> quickcheck(numtests(N, prop_sort())). 37 | 38 | % Test that merging two heaps results in the addition of their lists. 39 | prop_merge() -> 40 | ?FORALL({Xs, Ys}, {list(int), list(int)}, 41 | begin 42 | H1 = skewbinheap:insert_all(Xs, skewbinheap:new()), 43 | H2 = skewbinheap:insert_all(Ys, skewbinheap:new()), 44 | H = skewbinheap:merge(H1, H2), 45 | equals(ary(H), lists:sort(Xs ++ Ys)) 46 | end). 47 | test_merge(N) -> quickcheck(numtests(N, prop_merge())). 48 | -------------------------------------------------------------------------------- /skewbinheap.erl: -------------------------------------------------------------------------------- 1 | -module(skewbinheap). 2 | 3 | -export([ 4 | new/0, 5 | is_empty/1, 6 | merge/2, 7 | insert/2, 8 | insert_all/2, 9 | min/1, 10 | delete_min/1, 11 | find_delete_min/1 12 | ]). 13 | 14 | %% A tree is an integer, a value, a list of values, and a list of child trees. 15 | -type(tree(A) :: {integer(), A, [A], [tree(A)]}). 16 | 17 | %% A heap is a list of trees. 18 | -type heap(A) :: [tree(A)]. 19 | 20 | %% @doc Create a new heap. 21 | -spec new() -> heap(_). 22 | new() -> 23 | []. 24 | 25 | %% @doc Is the given heap empty? 26 | -spec is_empty(heap(_)) -> boolean(). 27 | is_empty([]) -> true; 28 | is_empty(_) -> false. 29 | 30 | %% The rank of a given node. 31 | -spec rank(tree(_)) -> integer(). 32 | rank({R, _X, _Xs, _C}) -> R. 33 | 34 | %% The root of a given node. 35 | -spec root(tree(X)) -> X. 36 | root({_R, X, _Xs, _C}) -> X. 37 | 38 | %% Link two trees together. 39 | -spec link(tree(X), tree(Y)) -> tree(X | Y). 40 | link({R, X1, Xs1, C1} = T1, {_, X2, Xs2, C2} = T2) -> 41 | if 42 | X1 =< X2 -> 43 | {R+1, X1, Xs1, [T2 | C1]}; 44 | true -> 45 | {R+1, X2, Xs2, [T1 | C2]} 46 | end. 47 | 48 | %% Link T1 and T2 together with a new root X. 49 | -spec skew_link(X, tree(Y), tree(Z)) -> tree(X | Y | Z). 50 | skew_link(X, T1, T2) -> 51 | {R, Y, Ys, C} = link(T1, T2), 52 | if 53 | X =< Y -> 54 | {R, X, [Y | Ys], C}; 55 | true -> 56 | {R, Y, [X | Ys], C} 57 | end. 58 | 59 | %% Insert a tree into a heap. 60 | -spec insert_tree(tree(X), heap(Y)) -> heap(X | Y). 61 | insert_tree(T, []) -> [T]; 62 | insert_tree(T1, [T2 | Ts]) -> 63 | case rank(T1) < rank(T2) of 64 | true -> 65 | [T1 | [T2 | Ts]]; 66 | false -> 67 | insert_tree(link(T1, T2), Ts) 68 | end. 69 | 70 | %% Merge two heaps together. 71 | -spec merge_trees(heap(X), heap(Y)) -> heap(X | Y). 72 | merge_trees(H, []) -> H; 73 | merge_trees([], H) -> H; 74 | merge_trees([X|Xs] = H1, [Y|Ys] = H2) -> 75 | case rank(X) < rank(Y) of 76 | true -> 77 | %% Prepend X 78 | [X | merge_trees(Xs, H2)]; 79 | false -> 80 | case rank(Y) < rank(X) of 81 | true -> 82 | %% Prepend Y 83 | [Y | merge_trees(H1, Ys)]; 84 | false -> 85 | %% Equal ranks 86 | insert_tree(link(X, Y), merge_trees(Xs, Ys)) 87 | end 88 | end. 89 | 90 | -spec normalize(heap(X)) -> heap(X). 91 | normalize([]) -> []; 92 | normalize([Head | Tail]) -> 93 | insert_tree(Head, Tail). 94 | 95 | %% @doc Insert an object into the heap. 96 | -spec insert(X, heap(Y)) -> heap(X | Y). 97 | insert(X, [T1 | [T2 | Rest]] = Ts) -> 98 | case rank(T1) == rank(T2) of 99 | true -> 100 | [skew_link(X, T1, T2) | Rest]; 101 | false -> 102 | [{0, X, [], []} | Ts] 103 | end; 104 | insert(X, Ts) -> 105 | %% Simply prepend a single-element tree. 106 | [{0, X, [], []} | Ts]. 107 | 108 | %% Merge two heaps. 109 | -spec merge(heap(X), heap(Y)) -> heap(X | Y). 110 | merge(T1, T2) -> 111 | merge_trees(normalize(T1), normalize(T2)). 112 | 113 | %% Remove minimum tree from a heap. 114 | -spec remove_min_tree(heap(X)) -> {tree(X), heap(X)} | no_return(). 115 | remove_min_tree([]) -> 116 | throw(empty); 117 | remove_min_tree([T]) -> 118 | {T, []}; 119 | remove_min_tree([T|Ts]) -> 120 | {T1, Ts1} = remove_min_tree(Ts), 121 | case root(T) =< root(T1) of 122 | true -> 123 | {T, Ts}; 124 | false -> 125 | {T1, [T | Ts1]} 126 | end. 127 | 128 | %% @doc Find minimum element. 129 | -spec min(heap(X)) -> X. 130 | min(Ts) -> 131 | {T, _} = remove_min_tree(Ts), 132 | root(T). 133 | 134 | %% @doc Insert a list of elements. 135 | -spec insert_all([X], heap(Y)) -> heap(X | Y). 136 | insert_all([], Ts) -> 137 | Ts; 138 | insert_all([X | Xs], Ts) -> 139 | insert_all(Xs, insert(X, Ts)). 140 | 141 | %% @doc Delete minimum element. 142 | -spec delete_min(heap(X)) -> heap(X). 143 | delete_min(Ts) -> 144 | {_Min, Deleted} = find_delete_min(Ts), 145 | Deleted. 146 | 147 | %% @doc Finds and deletes minimum element. 148 | %% @doc Returns {Min, HeapWithoutMin} 149 | -spec find_delete_min(heap(X)) -> {X, heap(X)}. 150 | find_delete_min(Ts) -> 151 | {{_, X, Xs, Ts1}, Ts2} = remove_min_tree(Ts), 152 | {X, insert_all(Xs, merge(lists:reverse(Ts1), Ts2))}. 153 | --------------------------------------------------------------------------------