Sorry if this is too late to be a legitimate entry.
James, what is the official way to submit an entry? If posting to the
list is good enough, here you go. :-)
My solution doesn't use recursion. It deterministically transforms
(heapindex, heapsize) coordinates into (x, y) coordinates. My only
gripe with this solution is that pre-initializing a 2D text buffer
beforehand looks kinda kludgy.
My full solution is at http://www.shinybit.com/ruby_quiz_40.rb , but
it's just the original code from James (with the bugfix addendum) with
three methods added. They are poorly named and could use some cleaning
up. Here is my output, followed by the three methods I added to the
Heap class.
I've used some kludgy mechanisms in my code. I would *love* to hear any
input on how to clean them up. If you spot an obviously missing Ruby
idiom, please let me know.
The output:
dbrady@gromit:~$ ruby ruby_quiz_40.rb
,- 35
,- 29
| `- 40
,- 20
| | ,- 26
| `- 23
| `- 51
12
| ,- 19
| ,- 17
| |
`- 15
|
`- 22
,- 35
,- 29
| `- 40
,- 20
| | ,- 26
| `- 23
| `- 51
12
| ,- 19
| ,- 15
| | `- 17
`- 13
|
`- 22
,- 35
,- 29
| `- 40
,- 20
| | ,- 26
| `- 23
| `- 51
13
| ,- 19
| ,- 17
| |
`- 15
|
`- 22
And here are the three methods I added to the Heap class:
# One bit of kludginess: The /= operator breaks indenting in emacs
# ruby-mode. This is why I use the x = x / 2 form instead.
# ----------------------------------------------------------------------
# get_depth
#
# returns the depth in the tree of a given 0-based index.
# num - 1-based index of element.
# returns - depth of the tree at this index. The root node has a
# depth of zero.
def get_depth(num)
(Math.log(num)/Math.log(2)).to_i
end
# ----------------------------------------------------------------------
# get_line
#
# for index num in a heap of depth rows, returns the line.
# num is 1-based index of element.
# returns 0-based line number, with 0 at the top (left-hand
# side). Sample tree, get_line(1) returns 7, etc.
def get_line(num)
depth = get_depth(@heap.length)
# Okay, scary algorithm. Odd-numbered indexes are down from their
# parents while even-numbered indexes are up from their parents.
# We want to move from this node, up the tree to the root,
# considering each node for even/oddness. At the leafmost level,
# the leaves move up or down 1 line. Each level you move up, the
# offset doubles, until node 1 is moved down 1/2 the size of the
# tree (because it's odd).
line = 0
while num > 0
offset = 2**(depth - get_depth(num))
if num % 2 == 1
line += offset
else
line -= offset
end
num = num / 2 # Integer divide to get parent index.
end
line-1
end
# ----------------------------------------------------------------------
# to_s
#
# converts the heap to an ascii art tree representation.
def to_s
output = []
# Initialize output as an array of strings. We're going to "draw"
into the
# output like it's a 2D text buffer, so we need to make sure the
output is
# initialized with indent strings. (By an odd coincidence, all of
ascii art
# is confined to the area to the left of each given string.
However, some
# elements are empty, so we need to make sure that all lines have
their left
# indent already buffered out.
#
# This hairy bit of math calculates the next largest power of 2 up
from @heap.length.
max_lines = 2**((Math.log(@heap.length)/Math.log(2)).to_i + 1)
max_indent = get_depth(max_lines)
(@heap.length).times { output.push(' ' * INDENT_AMOUNT * max_indent) }
@heap.each_with_index do |item, index|
next if index.zero? # @heap[0] appears to be a dummy node
output[get_line(index)] = ' ' * INDENT_AMOUNT * get_depth(index) +
item.to_s
end
# Okay, the tree nodes are now "drawn" into the output buffer.
# Now draw connecting lines. At each child node, draw ,- or `-
# into their left margin. Then bring down a column of |'s to
# the parent node.
#
# Note that since we're drilling "down" from the parent nodes, we
# only need to traverse the first half of the items in the tree.
# The careful observer might note that in the sample tree, node 7
# ("22") never gets traversed. This is okay; it gets skipped
# because we know it can't possibly have any children.
(@heap.length / 2).times do |i|
j = i+1 # The math is easier when done 1-based.
# this whole mess could be refactored; these 2 blocks
# differ only in start line, ,- vs `- text, and loop
# direction.
# connect node i to node i*2 (up from parent)
indent = get_depth(j*2) * INDENT_AMOUNT
stop_line = get_line(j)
next if j*2 >= @heap.length
start_line = get_line(j*2)
output[start_line][indent-3..indent-2] = ',-'
start_line += 1
while start_line < stop_line
output[start_line][indent-3] = '|'
start_line += 1
end
# connect node i to node i*2+1 (down from parent)
start_line = get_line(j*2+1)
indent = get_depth(j*2+1) * INDENT_AMOUNT
next if j*2+1 >= @heap.length
output[start_line][indent-3..indent-2] = '`-'
start_line -= 1
while start_line > stop_line
output[start_line][indent-3] = '|'
start_line -= 1
end
end
output.join("\n")
end
Cheers!
-dB
--
David Brady
ruby-talk / shinybit.com
I'm having a really surreal day... OR AM I?