diff --git a/lib/tree.rb b/lib/tree.rb index c0d4b51..a7a62e8 100644 --- a/lib/tree.rb +++ b/lib/tree.rb @@ -16,51 +16,225 @@ def initialize @root = nil end - # Time Complexity: - # Space Complexity: + # Time Complexity: O(log n) + # Space Complexity: O(n) def add(key, value) - raise NotImplementedError + @root = add_helper(@root, key, value) end - # Time Complexity: - # Space Complexity: + # def add(key, value) + # new_node = TreeNode.new(key, value) + # current = @root + # if current.nil? + # current = new_node + # else + # done = false + # until done + # if key <= current.key + # if !current.left.nil? + # current = current.left + # else + # current.left = new_node + # done = true + # end + # else + # if !current.right.nil? + # current = current.right + # else + # current.right = new_node + # done = true + # end + # end + # end + # end + # return current + # end + + # Time Complexity: O(log n) + # Space Complexity: O(n) def find(key) - raise NotImplementedError + return find_helper(@root, key) end - # Time Complexity: - # Space Complexity: + # def find(key) + # current_node = @root + # if current_node.nil? + # return nil + # else + # while true + # if current_node.key == key + # return current_node.value + # elsif current_node.key > key + # if current_node.left.nil? + # return false + # else + # current_node = current_node.left + # end + # else + # if current_node.right.nil? + # return false + # else + # current_node = current_node.right + # end + # end + # end + # end + # end + + # Time Complexity: O(n) + # Space Complexity: O(n) def inorder - raise NotImplementedError + inorder_helper(@root, []) end - # Time Complexity: - # Space Complexity: + # Time Complexity: O(n) + # Space Complexity: O(n) def preorder - raise NotImplementedError + preorder_helper(@root, []) end - # Time Complexity: - # Space Complexity: + # Time Complexity: O(n) + # Space Complexity: O(n) def postorder - raise NotImplementedError + postorder_helper(@root, []) end - # Time Complexity: - # Space Complexity: + # Time Complexity: O(n) + # Space Complexity: O(2n) or O(n) def height - raise NotImplementedError + return height_helper(@root) end # Optional Method - # Time Complexity: - # Space Complexity: + # Time Complexity: + # Space Complexity: def bfs raise NotImplementedError end + def delete(key) + parent = nil + current = @root + # find the node, keeping track of its parent + result = find_with_parent_helper(current, parent, key) + return nil if result.nil? + current = result[:current] + parent = result[:parent] + # if the found node has no children + # - if the node was parent.right, set parent.right to nil + # - if the node was parent.left, set parent.left to nil + if current.left.nil? && current.right.nil? + (parent.right == current) \ + ? parent.right = nil \ + : parent.left = nil + # else if the found node has 1 child + # - if child is .right, point parent.right to the child + elsif !current.right.nil? && current.left.nil? + (parent.right == current) \ + ? parent.right = current.right \ + : parent.left = current.right + # - if child is .left, point parent.left to the child + elsif !current.left.nil? && current.right.nil? + (parent.right == current) \ + ? parent.right = current.left \ + : parent.left = current.left + else + # else if the found node has 2 children + # - find the right child's leftmost leaf + right_child = current.right + leftmost_left = right_child.left + until leftmost_leaf.left.nil? + leftmost_leaf = leftmost_leaf.left + end + # - copy leftmost leaf to item to delete + current.value = leftmost_leaf.value + # - change right child's .left to point to leftmost leaf's .right + right_child.left = leftmost_leaf.right + # - garbage collector deletes current + end + end + # Useful for printing def to_s return "#{self.inorder}" end + + private + + def find_with_parent_helper(current_node, parent_node, key) + return nil if current_node.nil? + return {"current": current_node, "parent": parent_node} if current_node.key == key + + if key <= current_node.key + return find_with_parent_helper(current_node.left, current_node, key) + else + return find_with_parent_helper(current_node.right, current_node, key) + end + end + + def add_helper(current_node, key, value) + return TreeNode.new(key, value) if current_node.nil? + + if key <= current_node.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 find_helper(current_node, key) + return nil if current_node.nil? + return current_node.value if current_node.key == key + + if key <= current_node.key + return find_helper(current_node.left, key) + else + return find_helper(current_node.right, key) + end + end + + def inorder_helper(current_node, list) + return list if current_node.nil? + + 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 preorder_helper(current_node, list) + return list if current_node.nil? + + 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 postorder_helper(current_node, list) + return list if current_node.nil? + + 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 height_helper(current_node) + return 0 if current_node.nil? + + left_height = height_helper(current_node.left) + right_height = height_helper(current_node.right) + + if left_height >= right_height + return left_height + 1 + else + return right_height + 1 + end + end end diff --git a/test/tree_test.rb b/test/tree_test.rb index 345bf66..a33dd53 100644 --- a/test/tree_test.rb +++ b/test/tree_test.rb @@ -13,7 +13,7 @@ tree } - describe "add and find" do + describe "add and find" do it "add & find values" do tree.add(5, "Peter") expect(tree.find(5)).must_equal "Peter" @@ -28,6 +28,12 @@ it "can't find anything when the tree is empty" do expect(tree.find(50)).must_be_nil end + + it "can't find anything when the tree doesn't have the item" do + tree.add(5, "Peter") + tree.add(15, "Ada") + expect(tree.find(50)).must_be_nil + end end describe "inorder" do @@ -36,8 +42,8 @@ end it "will return the tree in order" do - expect(tree_with_nodes.inorder).must_equal [{:key=>1, :value=>"Mary"}, {:key=>3, :value=>"Paul"}, - {:key=>5, :value=>"Peter"}, {:key=>10, :value=>"Karla"}, + expect(tree_with_nodes.inorder).must_equal [{:key=>1, :value=>"Mary"}, {:key=>3, :value=>"Paul"}, + {:key=>5, :value=>"Peter"}, {:key=>10, :value=>"Karla"}, {:key=>15, :value=>"Ada"}, {:key=>25, :value=>"Kari"}] end end @@ -49,8 +55,8 @@ end it "will return the tree in preorder" do - expect(tree_with_nodes.preorder).must_equal [{:key=>5, :value=>"Peter"}, {:key=>3, :value=>"Paul"}, - {:key=>1, :value=>"Mary"}, {:key=>10, :value=>"Karla"}, + expect(tree_with_nodes.preorder).must_equal [{:key=>5, :value=>"Peter"}, {:key=>3, :value=>"Paul"}, + {:key=>1, :value=>"Mary"}, {:key=>10, :value=>"Karla"}, {:key=>15, :value=>"Ada"}, {:key=>25, :value=>"Kari"}] end end @@ -61,26 +67,26 @@ end it "will return the tree in postorder" do - expect(tree_with_nodes.postorder).must_equal [{:key=>1, :value=>"Mary"}, {:key=>3, :value=>"Paul"}, - {:key=>25, :value=>"Kari"}, {:key=>15, :value=>"Ada"}, + expect(tree_with_nodes.postorder).must_equal [{:key=>1, :value=>"Mary"}, {:key=>3, :value=>"Paul"}, + {:key=>25, :value=>"Kari"}, {:key=>15, :value=>"Ada"}, {:key=>10, :value=>"Karla"}, {:key=>5, :value=>"Peter"}] end end - describe "breadth first search" do + xdescribe "breadth first search" do it "will give an empty array for an empty tree" do expect(tree.bfs).must_equal [] end it "will return an array of a level-by-level output of the tree" do - expect(tree_with_nodes.bfs).must_equal [{:key=>5, :value=>"Peter"}, {:key=>3, :value=>"Paul"}, - {:key=>10, :value=>"Karla"}, {:key=>1, :value=>"Mary"}, + 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"}] end end - - describe "height" do - it "will return 0 if tree is empty" do + + describe "height" do + it "will return 0 if tree is empty" do expect(tree.height()).must_equal 0 end @@ -91,7 +97,7 @@ tree_with_nodes.add(65, "sam") expect(tree_with_nodes.height).must_equal 6 end - + it "will give the correct height of a binary search tree" do tree_with_nodes.add(30, "Tatiana") expect(tree_with_nodes.height).must_equal 5