fork download
  1. # print_canvas3のバグを修正して整理しました
  2. class Node
  3. attr_accessor :value, :left, :right
  4. def initialize(val)
  5. @value = val # ノードが保持する値
  6. @left = nil # 左側のノード
  7. @right = nil # 右側のノード
  8. end
  9. end
  10.  
  11. # 描画位置の決まったノード
  12. RenderedNode = Struct.new(:x, :y, :node) do
  13. # ノードの描画に必要な幅
  14. def width
  15. node.value.to_s.size
  16. end
  17. end
  18.  
  19. # 各ノードの位置を計算する
  20. class Renderer
  21. def initialize(margin=0)
  22. @margin = margin
  23. end
  24.  
  25. # render_infoで描画情報を集めて返す (外部用)
  26. def render(root)
  27. @nodes = []
  28. render_info(root, 0, 0)
  29. @nodes
  30. end
  31.  
  32. private
  33.  
  34. # 描画に必要な情報を計算 (内部用)
  35. def render_info(node, x, y)
  36. # 左のノードがあれば、そのノード以下の木の横幅が返ってくる
  37. x = render_info(node.left, x, y + 1) + @margin if node.left
  38.  
  39. # このノードの分の描画に必要な情報を追加
  40. @nodes << RenderedNode.new(x, y, node)
  41.  
  42. # このノードの分の描画に必要な情報を追加
  43. x += node.value.to_s.size
  44. x = render_info(node.right, x + @margin, y + 1) if node.right
  45.  
  46. # 呼び出した親のために横幅を返す
  47. x
  48. end
  49. end
  50.  
  51. # Rendererの返した座標データから二分木のAAを表示する
  52. class Printer
  53. # コンストラクタで座標データをうけとる
  54. def initialize(nodes)
  55. @lines = group_by_line(nodes)
  56. @slashes = nil
  57. end
  58.  
  59. # 旧print_canvas
  60. def run
  61. @slashes = []
  62. @lines.each do |line|
  63. print_numbers(line)
  64. print_slashes
  65. @slashes.clear
  66. end
  67. end
  68.  
  69. private
  70.  
  71. # 座標データを行ごとにグループ分け
  72. def group_by_line(nodes)
  73. nodes.group_by {|n| n.y }.sort.map {|_, line| line.sort_by {|n| n.x } }
  74. end
  75.  
  76. # 数字と下線からなる行の表示
  77. def print_numbers(line)
  78. last_x = 0
  79.  
  80. line.each do |n|
  81. x = print_left_space(n, last_x)
  82. x = print_node(n, x)
  83. x = print_right_space(n, x)
  84. last_x = x
  85. end
  86.  
  87. puts
  88. end
  89.  
  90. # 数字左の空白と下線の表示
  91. # 右端の座標(現在のカーソル位置)を返す
  92. def print_left_space(n, last_x)
  93. x = n.x
  94.  
  95. if left_child = find_left_child(n)
  96. left_x_end = left_child.x + left_child.width
  97.  
  98. print " " * (left_x_end - last_x)
  99. if left_x_end < x
  100. print " ", "_" * (x - left_x_end - 1)
  101.  
  102. # 斜線の描画を予約 (左手ノードなのでスラッシュ)
  103. @slashes << [left_x_end, '/']
  104. end
  105. else
  106. print " " * (x - last_x)
  107. end
  108.  
  109. x
  110. end
  111.  
  112. # 数字本体の表示
  113. # 右端の座標(現在のカーソル位置)を返す
  114. def print_node(n, x)
  115. print n.node.value
  116. x + n.width
  117. end
  118.  
  119. # 数字右の空白と下線の表示
  120. # 右端の座標(現在のカーソル位置)を返す
  121. def print_right_space(n, x)
  122. if right_child = find_right_child(n)
  123. right_x = right_child.x
  124. if x < right_x
  125. print "_" * (right_x - x - 1), " "
  126.  
  127. # 斜線の描画を予約 (右手ノードなのでバックスラッシュ)
  128. @slashes << [right_x - 1, '\\']
  129. end
  130. x = right_x
  131. end
  132. x
  133. end
  134.  
  135. # 斜線からなる行の表示
  136. def print_slashes
  137. last_x = 0
  138. @slashes.each do |x, char|
  139. print " " * (x - last_x)
  140. print char
  141. last_x = x + 1
  142. end
  143. puts unless @slashes.empty?
  144. end
  145.  
  146. # 右手の子ノードを探す
  147. def find_right_child(n)
  148. @lines[n.y + 1][index_right_child(n)] if n.node.right
  149. end
  150.  
  151. # 左手の子ノードを探す
  152. def find_left_child(n)
  153. if n.node.left
  154. next_line = @lines[n.y + 1]
  155.  
  156. # * バグ修正部分 *
  157. # 右手のノードを探してからそのすぐ左を取る方法なので
  158. # 右手にノードがない場合に失敗する
  159. i = index_right_child(n)
  160. if i
  161. next_line[i - 1]
  162. else
  163. next_line.last
  164. end
  165. end
  166. end
  167.  
  168. # 右手の子ノードの添字を見つける
  169. def index_right_child(n)
  170. next_line = @lines[n.y + 1]
  171. next_line.index {|m| n.x <= m.x }
  172. end
  173. end
  174.  
  175. # ツリーの宣言を短く書くために定義
  176. def Node(val, left=nil, right=nil)
  177. node = Node.new(val)
  178. node.left = left
  179. node.right = right
  180. node
  181. end
  182.  
  183. # AAを描画
  184. def draw_tree(root_node, margin)
  185. nodes = Renderer.new(margin).render(root_node)
  186. Printer.new(nodes).run
  187. end
  188.  
  189. tree = Node(9, Node(4, Node(2), Node(6)), Node(15, Node(12), Node(17)))
  190. draw_tree Node(4, tree, Node(3, tree, nil)), 1
  191.  
  192. __END__
  193. _________4_________________
  194. / \
  195. __9___ _________3
  196. / \ /
  197. 4 15 __9___
  198. / \ / \ / \
  199. 2 6 12 17 4 15
  200. / \ / \
  201. 2 6 12 17
Success #stdin #stdout 0.01s 7476KB
stdin
Standard input is empty
stdout
        _________4_________________ 
       /                           \
    __9___                 _________3
   /      \               /
  4        15          __9___ 
 / \      /  \        /      \
2   6   12    17     4        15 
                    / \      /  \
                   2   6   12    17