Trees
Preorder Traversal
Visit the root before each subtree, producing root-left-right order.
Algorithm
The canonical tree is 4(2(1,3),6(5,7)), so this Ruby DSA
implementation can be compared directly with the rest of the DSA track.
preorder
Preorder records the current node before visiting left and right subtrees.
Basic Implementation
basic.rb
Replay: real traced execution (multi-file project)
class Node
attr_accessor :value, :left, :right
def initialize(value, left = nil, right = nil)
@value = value
@left = left
@right = right
end
end
def render(node)
return "_" if node.nil?
return node.value.to_s if node.left.nil? && node.right.nil?
"#{node.value}(#{render(node.left)},#{render(node.right)})"
end
def sample_tree
Node.new(4, Node.new(2, Node.new(1), Node.new(3)), Node.new(6, Node.new(5), Node.new(7)))
end
def preorder(node, output); return if node.nil?; output << node.value; preorder(node.left, output); preorder(node.right, output); end
output = []
preorder(sample_tree, output)
puts "[#{output.join(', ')}]"
tree ← 4(2(1,3),6(5,7)), output ← []
1class Node2 attr_accessor :value, :left, :rightvalues this step4(2(1,3),6(5,7))tree[]outputoutput ← [4]
16end17def preorder(node, output); return if node.nil?; output << node.value; preorder(node.left, output); preorder(node.right, output); end18output = []values this step[] → [4]output4nodeoutput ← [4, 2]
16end17def preorder(node, output); return if node.nil?; output << node.value; preorder(node.left, output); preorder(node.right, output); end18output = []values this step[4] → [4, 2]output2nodeoutput ← [4, 2, 1]
16end17def preorder(node, output); return if node.nil?; output << node.value; preorder(node.left, output); preorder(node.right, output); end18output = []values this step[4, 2] → [4, 2, 1]output1nodeoutput ← [4, 2, 1, 3]
16end17def preorder(node, output); return if node.nil?; output << node.value; preorder(node.left, output); preorder(node.right, output); end18output = []values this step[4, 2, 1] → [4, 2, 1, 3]output3nodeoutput ← [4, 2, 1, 3, 6]
16end17def preorder(node, output); return if node.nil?; output << node.value; preorder(node.left, output); preorder(node.right, output); end18output = []values this step[4, 2, 1, 3] → [4, 2, 1, 3, 6]output6nodeoutput ← [4, 2, 1, 3, 6, 5]
16end17def preorder(node, output); return if node.nil?; output << node.value; preorder(node.left, output); preorder(node.right, output); end18output = []values this step[4, 2, 1, 3, 6] → [4, 2, 1, 3, 6, 5]output5nodeoutput ← [4, 2, 1, 3, 6, 5, 7]
16end17def preorder(node, output); return if node.nil?; output << node.value; preorder(node.left, output); preorder(node.right, output); end18output = []values this step[4, 2, 1, 3, 6, 5] → [4, 2, 1, 3, 6, 5, 7]output7nodeclass Node
1class Node2 attr_accessor :value, :left, :rightvalues this step[4, 2, 1, 3, 6, 5, 7]output
Complexity
- Time: O(n)
- Space: O(h) recursion stack
Implementation notes
Nodeis the same Ruby class shape as the tree-build lesson, withattr_accessor :value, :left, :rightandnilfor missing children.preorder(node, output)is recursive and returns immediately withreturn if node.nil?.- The method mutates one shared Ruby array,
output, instead of returning a new array from each call. - Visit happens before either child call:
output << node.value, thenpreorder(node.left, output), thenpreorder(node.right, output). - That call order is visible in the trace as
4,2,1,3, then6,5,7. - The output array grows step by step from
[]to[4, 2, 1, 3, 6, 5, 7]. puts "[#{output.join(', ')}]"formats the final preorder as the bracketed list[4, 2, 1, 3, 6, 5, 7].