Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added heappeek, heappushpop!, heapreplace! to collections, + tests #10810

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions base/collections.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ export
enqueue!,
heapify!,
heapify,
heappeek,
heappop!,
heappush!,
heappushpop!,
heapreplace!,
isheap,
peek

Expand Down Expand Up @@ -59,6 +62,7 @@ end

percolate_up!{T}(xs::AbstractArray{T}, i::Integer, o::Ordering) = percolate_up!(xs, i, xs[i], o)

heappeek(xs::AbstractArray) = xs[1]

# Binary min-heap pop.
function heappop!(xs::AbstractArray, o::Ordering=Forward)
Expand All @@ -79,6 +83,24 @@ function heappush!(xs::AbstractArray, x, o::Ordering=Forward)
end


# Binary min-heap pushpop (faster than push followed by pop, as only one percolate).
function heappushpop!(xs::AbstractArray, x, o::Ordering=Forward)
if !isempty(xs) && lt(o, xs[1], x)
xs[1], x = x, xs[1]
percolate_down!(xs, 1, o)
end
x
end


# Binary min-heap replace (faster than pop followed by push, as only one percolate).
function heapreplace!(xs::AbstractArray, x, o::Ordering=Forward)
xs[1] = x
percolate_down!(xs, 1, x, o)
xs
end


# Turn an arbitrary array into a binary min-heap in linear time.
function heapify!(xs::AbstractArray, o::Ordering=Forward)
for i in heapparent(length(xs)):-1:1
Expand Down
27 changes: 24 additions & 3 deletions doc/stdlib/collections.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1152,10 +1152,10 @@ is used, so that elements popped from the heap are given in ascending order.

Return true iff an array is heap-ordered according to the given order.

.. function:: heappush!(v, x, [ord])
.. function:: heappeek(v)

Given a binary heap-ordered array, push a new element ``x``, preserving the heap
property. For efficiency, this function does not check that the array is
Given a binary heap-ordered array, return the lowest ordered
element. For efficiency, this function does not check that the array is
indeed heap-ordered.

.. function:: heappop!(v, [ord])
Expand All @@ -1164,3 +1164,24 @@ is used, so that elements popped from the heap are given in ascending order.
element. For efficiency, this function does not check that the array is
indeed heap-ordered.

.. function:: heappush!(v, x, [ord])

Given a binary heap-ordered array, push a new element ``x``, preserving the heap
property. For efficiency, this function does not check that the array is
indeed heap-ordered.

.. function:: heappushpop!(v, x, [ord])

Given a binary heap-ordered array, push a new element ``x``, preserving the heap
property, and then remove and return the lowest ordered element. Equivalent to
a push followed by a pop, but more efficient as the heap property is restored
only once. For efficiency, this function does not check that the array is
indeed heap-ordered.

.. function:: heapreplace!(v, x, [ord])

Given a binary heap-ordered array, replace the lowest ordered element with
a new element ``x``, preserving the heap property. Equivalent to
a pop followed by a push, but more efficient as the heap property is restored
only once. For efficiency, this function does not check that the array is
indeed heap-ordered.
56 changes: 56 additions & 0 deletions test/priorityqueue.jl
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,59 @@ for priority in values(priorities)
heappush!(xs, priority)
end
@test issorted([heappop!(xs) for _ in length(priorities)])


# test heap operations (heapify, heappeek, heappop!, heappush!, heappushpop!, heapreplace!)
maxheapsize = 7
for heapsize = 1:maxheapsize
# heapifying all permutations is a bit of overkill (how much? see http:https://oeis.org/A132862 :) but easy
# for each heap, we pop an element, or push/pushpop/replace a new element, and check that invariants still hold (heap property etc.)
for perm in permutations(collect(2:2:2heapsize))
xs_ref = heapify(perm)
xs = copy(xs_ref)
@test isheap(xs)
@test heappeek(xs) == 2

x = heappop!(xs)
@test isheap(xs)
@test x == 2

for x in 1:(2*heapsize+1)
xs = copy(xs_ref)
heappush!(xs, x)
@test isheap(xs)
@test heappeek(xs) == min(x, 2)
@test x in xs

xs = copy(xs_ref)
y = heappushpop!(xs, x) # push then pop
@test isheap(xs)
if x <= 2 # we pushed and popped it right back
@test y == x
@test xs == xs_ref
else
@test y == 2
@test x in xs
end

xs = copy(xs_ref)
heapreplace!(xs, x) # pop then push
@test isheap(xs)
@test x in xs
if heapsize == 1
@test heappeek(xs) == x
else
@test heappeek(xs) == min(x, 4)
end
end
end
end

# edgecases
xs = Int64[]
@test isheap(xs)
@test_throws BoundsError heappeek(xs)
@test_throws BoundsError heappop!(xs)
@test_throws BoundsError heapreplace!(xs, 5)
@test heappushpop!(xs, 5) == 5
@test isempty(xs)