Current section
Files
Jump to
Current section
Files
lib/bheap.ex
defmodule BHeap do
@moduledoc """
Binomial heaps in Elixir.
For general information about them, see [Wikipedia](https://en.wikipedia.org/wiki/Binomial_heap).
The collection of binomial trees satisfies the following conditions:
* Each binomial tree in a heap obeys the minimum-heap property: the key of a node is greater than or equal to the key of its parent
* There can only be either one or zero binomial trees for each order, including zero order
"""
alias BHeap.BTree
@empty []
@doc """
Creates a new heap.
## Example
iex> BHeap.new()
[]
"""
def new, do: @empty
@doc """
Creates a new heap from a given enumerable.
## Example
iex> BHeap.new([1, 2])
[{1, 1, [{0, 2, []}]}]
"""
def new(enumerable) do
enumerable
|> Enum.reduce(@empty, &(put(&2,&1)))
end
@doc """
Puts a new value in a heap.
## Example
iex> BHeap.new() |> BHeap.put(1) |> BHeap.put(2)
[{1, 1, [{0, 2, []}]}]
"""
def put(bheap, value) do
bheap
|> put_tree_in_list(BTree.new(value))
end
@doc """
Sorts the given heap and returns a list.
## Example
iex> heap1 = BHeap.new([2, 4, 6, 8, 10])
iex> heap2 = BHeap.new([1, 3, 5, 7, 9])
iex> BHeap.merge(heap1, heap2) |> BHeap.sort()
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
"""
def sort(heap), do: sort(heap, [])
defp sort(@empty, mins), do: Enum.reverse(mins)
defp sort(heap, mins) do
heap
|> remove_min()
|> sort([min(heap) | mins])
end
@doc """
Returns the minimum element of a heap.
## Example
iex> BHeap.new([10, 1, 7]) |> BHeap.min()
1
"""
def min(bheap) do
bheap
|> Enum.map(fn({_, value, _}) -> value end)
|> Enum.min()
end
@doc """
Removes the minimum element from a heap.
## Example
iex> BHeap.new([10, 1, 7]) |> BHeap.remove_min()
[{1, 7, [{0, 10, []}]}]
"""
def remove_min(bheap) do
{{_, _, rest}, rest_heaps} = remove_min_tree(bheap)
rest
|> Enum.reverse()
|> merge(rest_heaps)
end
defp remove_min_tree([tree]), do: {tree, []}
defp remove_min_tree([{_, value, _} = tree | rest]) do
{{_, inner_val, _} = inner_tree, inner_rest} = remove_min_tree(rest)
if value < inner_val do
{tree, rest}
else
{inner_tree, [tree | inner_rest]}
end
end
@doc """
Merges two heaps.
## Example
iex> heap1 = BHeap.new([2, 4, 6])
iex> heap2 = BHeap.new([1, 3, 5])
iex> BHeap.merge(heap1, heap2)
[{1, 5, [{0, 6, []}]}, {2, 1, [{1, 2, [{0, 4, []}]}, {0, 3, []}]}]
"""
def merge(bheap, @empty), do: bheap
def merge(@empty, bheap), do: bheap
def merge([tree1 | rest1] = bheap1, [tree2 | rest2] = bheap2) do
cond do
BTree.rank(tree1) < BTree.rank(tree2) ->
[tree1 | merge(rest1, bheap2)]
BTree.rank(tree1) > BTree.rank(tree2) ->
[tree2 | merge(bheap1, rest2)]
true ->
put_tree_in_list(merge(rest1, rest2), BTree.link(tree1, tree2))
end
end
defp put_tree_in_list([], tree), do: [tree]
defp put_tree_in_list([first | rest] = all, tree) do
if BTree.rank(tree) < BTree.rank(first) do
[tree | all]
else
put_tree_in_list(rest, BTree.link(tree, first))
end
end
end