Skip to content
Open
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
231 changes: 200 additions & 31 deletions lib/tree.rb
Original file line number Diff line number Diff line change
@@ -1,66 +1,235 @@
class TreeNode
attr_reader :key, :value
attr_accessor :left, :right

def initialize(key, val)
def initialize(key, val)
@key = key
@value = val
@left = nil
@right = nil
end
end
end

class Tree
attr_reader :root
def initialize
@root = nil
end

# Time Complexity:
# Space Complexity:
# Time Complexity: O(logn) in best case
# Space Complexity: O(1)
def add(key, value)
Comment on lines +19 to 21

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

raise NotImplementedError
current_and_parent_pair = find_current_and_parent_nodes(key)
if current_and_parent_pair[:current]
# update new value if key exists
current_and_parent_pair[:current].value = value
else
new_node = TreeNode.new(key,value)
parent = current_and_parent_pair[:parent]
link_node_to_parent(parent, new_node)
end
end

# Time Complexity:
# Space Complexity:

# IN CLASS PRACTICE: add() recursive
# def add_helper(current_node, key, value)
# return TreeNode.new(key, value) if !current_node
# if current_node.key > key
# current_node.left = add_helper(current_node.left, key, value)
# else
# current_node.right = add_helper(current_node.right, key, value)
# end
# return current_node
# end

# def add(key, value)
# @root = add_helper(@root, key, value)
# end

# Time Complexity: O(logn) in best case
# Space Complexity: O(1)
def find(key)
Comment on lines +48 to 50

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

raise NotImplementedError
current_and_parent_pair = find_current_and_parent_nodes(key)
if current_and_parent_pair[:current]
return current_and_parent_pair[:current].value
else
return nil
end
end

# Time Complexity:
# Space Complexity:
# Time Complexity: O(n) with n is the number of nodes in the tree
# Space Complexity: O(n)
def inorder
Comment on lines +59 to 61

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nicely done with a loop!

raise NotImplementedError
list = []
stack = []

current = @root
while current
stack << current
current = current.left
end

while !stack.empty?
top = stack.pop
list << {key: top.key, value: top.value}

if top.right
stack << top.right

current = top.right.left
while current
stack << current
current = current.left
end
end
end

return list
end

# Time Complexity:
# Space Complexity:

# IN CLASS PRACTICE: inorder() recursive
# def inorder_helper(current_node, list = [])
# return list if !current_node
# inorder_helper(current_node.left, list)
# list << {key: current_node.key, value: current_node.value}
# inorder_helper(current_node.right, list)
# return list
# end

# def inorder
# return inorder_helper(@root)
# end

# Time Complexity: O(n) with n is the number of nodes in the tree
# Space Complexity: O(n)
def preorder_helper(current_node, list = [])
return list if !current_node
list << {key: current_node.key, value: current_node.value}
preorder_helper(current_node.left, list)
preorder_helper(current_node.right, list)
return list
end

def preorder

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

raise NotImplementedError
return preorder_helper(@root)
end

# Time Complexity:
# Space Complexity:

# Time Complexity: O(n) with n is the number of nodes in the tree
# Space Complexity: O(n)
def postorder_helper(current_node, list = [])
return list if !current_node
postorder_helper(current_node.left, list)
postorder_helper(current_node.right, list)
list << {key: current_node.key, value: current_node.value}
return list
end

def postorder

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

raise NotImplementedError
return postorder_helper(@root)
end

# Time Complexity:
# Space Complexity:

# Time Complexity: O(n) with n is the number of nodes in the tree
# Space Complexity: O(logn) which is also the height of the tree.
def height_helper(current_node, level = 0)
Comment on lines +130 to +132

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Nicely done!

return level if !current_node
level = [height_helper(current_node.left, level + 1), height_helper(current_node.right, level + 1)].max
return level
end

def height
raise NotImplementedError
return height_helper(@root)
end

# Optional Method
# Time Complexity:
# Space Complexity:
# Time Complexity: O(n)
# Space Complexity: O(n)
def bfs
Comment on lines +143 to 145

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

raise NotImplementedError
list = []
if @root
queue = []
queue << @root
queue.each do |current|
list << {key: current.key, value: current.value}
queue << current.left if current.left
queue << current.right if current.right
end
end
return list
end

def delete(key)
current_and_parent_pair = find_current_and_parent_nodes(key)
current = current_and_parent_pair[:current]
parent = current_and_parent_pair[:parent]
if current
remove_child(parent, current)
new_subtree = nil
# rearrange new subtree from children if current is not a leaf node
if current.left || current.right
left = current.left
right = current.right
right_subtree_leftmost = find_leftmost_node(right)
right_subtree_leftmost.left = left if right_subtree_leftmost

new_subtree = right_subtree_leftmost ? right : left
end

link_node_to_parent(parent, new_subtree)
end
end

# Useful for printing
def to_s
return "#{self.inorder}"
end

private
def find_current_and_parent_nodes(key)
current = @root
parent = nil

while current
if current.key == key
return {current: current, parent: parent}
elsif current.key > key
parent = current
current = current.left
else
parent = current
current = current.right
end
end

return {current: current, parent: parent}
end

def find_leftmost_node(current_node)
leftmost = current_node
while leftmost && leftmost.left
leftmost = leftmost.left
end
return leftmost
end

def link_node_to_parent(parent, node)
return if !node
if !parent
@root = node
elsif parent.key > node.key
parent.left = node
else
parent.right = node
end
end

def remove_child(parent, current)
if !parent
@root = nil
elsif current
if parent.key > current.key
parent.left = nil
else
parent.right = nil
end
end
end
end
60 changes: 60 additions & 0 deletions test/tree_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,5 +118,65 @@
expect(answer).must_be_nil
expect(tree_with_nodes.find(47)).must_be_nil
end

it "can delete a leaf node" do
expect(tree_with_nodes.find(1)).must_equal "Mary"
expect(tree_with_nodes.find(25)).must_equal "Kari"

tree_with_nodes.delete(1)
expect(tree_with_nodes.find(1)).must_be_nil

tree_with_nodes.delete(25)
expect(tree_with_nodes.find(25)).must_be_nil
end

it "can delete the only node of a tree" do
tree_with_one_nodes = Tree.new()
tree_with_one_nodes.add(5, "Peter")
# Arrange & Assert
expect(tree_with_one_nodes.find(5)).must_equal "Peter"

# Act
tree_with_one_nodes.delete(5)

# Assert
expect(tree_with_one_nodes.find(5)).must_be_nil
end

it "can delete the node with only left subtree" do
# Arrange & Assert
expect(tree_with_nodes.find(3)).must_equal "Paul"
expect(tree_with_nodes.bfs).must_equal [{:key=>5, :value=>"Peter"}, {:key=>3, :value=>"Paul"},
{:key=>10, :value=>"Karla"}, {:key=>1, :value=>"Mary"},
{:key=>15, :value=>"Ada"}, {:key=>25, :value=>"Kari"}]

# Act
tree_with_nodes.delete(3)

# Assert
expect(tree_with_nodes.find(3)).must_be_nil
expect(tree_with_nodes.bfs).must_equal [{:key=>5, :value=>"Peter"}, {:key=>1, :value=>"Mary"},
{:key=>10, :value=>"Karla"},{:key=>15, :value=>"Ada"},
{:key=>25, :value=>"Kari"}]
end

it "can delete the node with only right subtree" do
# Arrange & Assert
expect(tree_with_nodes.find(10)).must_equal "Karla"
expect(tree_with_nodes.bfs).must_equal [{:key=>5, :value=>"Peter"}, {:key=>3, :value=>"Paul"},
{:key=>10, :value=>"Karla"}, {:key=>1, :value=>"Mary"},
{:key=>15, :value=>"Ada"}, {:key=>25, :value=>"Kari"}]

# Act
tree_with_nodes.delete(10)

# Assert
expect(tree_with_nodes.find(10)).must_be_nil
expect(tree_with_nodes.find(3)).must_equal "Paul"

expect(tree_with_nodes.bfs).must_equal [{:key=>5, :value=>"Peter"}, {:key=>3, :value=>"Paul"},
{:key=>15, :value=>"Ada"}, {:key=>1, :value=>"Mary"},
{:key=>25, :value=>"Kari"}]
end
end
end