Skip to content

Instantly share code, notes, and snippets.

@ilkelma
Created December 19, 2012 06:04
Show Gist options
  • Save ilkelma/4334738 to your computer and use it in GitHub Desktop.
Save ilkelma/4334738 to your computer and use it in GitHub Desktop.
Jumpstart Node.js' BinaryHeap implementation rewritten in coffeescript
exchange = require './exchange'
module.exports = BinaryHeap
BinaryHeap = (scoreFunction, options) ->
@content = []
if options
@options = options
else
@options = {}
@scoreFunction = scoreFunction
class BinaryHeap
all: () ->
@content
peek: () ->
if @options.max
return -@content[0]
else
return @content[0]
push: (element) ->
# Add the new element to the end of the array.
if @options.max
@content.push(-element)
else
@content.push(element)
# Allow it to bubble up.
@bubbleUp(@content.length - 1)
pop: () ->
# Store the first element so we can return it later.
result = @content[0]
# Get the element at the end of the array.
end = @content.pop()
# If there are any elements left, put the end element at the
# start, and let it sink down.
if @content.length > 0
@content[0] = end
@sinkDown(0)
if @options.max
return -result
else
return result
remove: (node) ->
if (exchange.BUY == @orderType) then node = -node
len = @content.length
# To remove a value, we must search through the array to find it.
for i in len by 1
if @content[i] == node
# When it is found, the process seen in 'pop' is repeated
# to fill up the hole.
end = @content.pop()
if (i != len - 1)
@content[i] = end
if @scoreFunction(end) < @scoreFunction(node)
@bubbleUp(i)
else
@sinkDown(i)
return
throw new Error("Node not found.")
size: () ->
return @content.length
bubbleUp: (n) ->
# Fetch the element that has to be moved.
element = @content[n]
# When at 0, an element can not go up any further.
while (n > 0)
# Compute the parent element's index, and fetch it.
parentN = Math.floor((n + 1) / 2) - 1
parent = @content[parentN]
# Swap the elements if the parent is greater.
if @scoreFunction(element) < @scoreFunction(parent)
@content[parentN] = element
@content[n] = parent
# Update 'n' to continue at the new position.
n = parentN
# Found a parent that is less, no need to move it further.
else
break
sinkDown: (n) ->
# Look up the target element and its score.
length = @content.length
element = @content[n]
elemScore = @scoreFunction(element)
while(true)
# Compute the indices of the child elements.
child2N = (n + 1) * 2
child1N = child2N - 1
# This is used to store the new position of the element,
# if any.
swap = null
# If the first child exists (is inside the array)...
if child1N < length
# Look it up and compute its score.
child1 = @content[child1N]
child1Score = @scoreFunction(child1)
# If the score is less than our element's, we need to swap.
if child1Score < elemScore
swap = child1N
# Do the same checks for the other child.
if child2N < length
child2 = @content[child2N]
child2Score = @scoreFunction(child2)
if (child2Score < (swap == null ? elemScore : child1Score))
swap = child2N
# If the element needs to be moved, swap it, and continue.
if (swap != null)
@content[n] = @content[swap]
@content[swap] = element
n = swap
# Otherwise, we are done.
else
break
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment