Skip to content

✨ Add ruby BST #334

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 11, 2023
Merged
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1776,8 +1776,8 @@ In order to achieve greater coverage and encourage more people to contribute to
</a>
</td>
<td> <!-- Ruby -->
<a href="./CONTRIBUTING.md">
<img align="center" height="25" src="./logos/github.svg" />
<a href="./src/ruby/binary_search_tree.rb">
<img align="center" height="25" src="./logos/ruby.svg" />
</a>
</td>
<td> <!-- JavaScript -->
Expand Down
215 changes: 215 additions & 0 deletions src/ruby/binary_search_tree.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
# frozen_string_literal: true

class BinarySearchTree
class Node
attr_accessor :left, :right, :value

def initialize(value)
@left = nil
@right = nil
@value = value
end
end

def initialize
@root = nil
end

attr_reader :root

def insert(value)
new_node = Node.new(value)

if @root.nil?
@root = new_node
return self
end

current_node = @root

loop do
if value < current_node.value
if current_node.left.nil?
current_node.left = new_node
return self
end
current_node = current_node.left
else
if current_node.right.nil?
current_node.right = new_node
return self
end
current_node = current_node.right
end
end

self
end

def lookup(value)
return nil if @root.nil?

current_node = @root

until current_node.nil?
if value < current_node.value
current_node = current_node.left
elsif value > current_node.value
current_node = current_node.right
else
return current_node
end
end

nil
end

def remove(value)
return false if @root.nil?

current_node = @root
parent_node = nil

while current_node
if value < current_node.value
parent_node = current_node
current_node = current_node.left
elsif value > current_node.value
parent_node = current_node
current_node = current_node.right
elsif current_node.value == value
# We have a match, get to work!

if current_node.right.nil?
update_parent_link(parent_node, current_node, current_node.left)
elsif current_node.right.left.nil?
current_node.right.left = current_node.left
update_parent_link(parent_node, current_node, current_node.right)
else
leftmost, leftmost_parent = find_leftmost(current_node.right)
leftmost_parent.left = leftmost.right
leftmost.left = current_node.left
leftmost.right = current_node.right
update_parent_link(parent_node, current_node, leftmost)
end

return true
end
end

false
end

def traverse(node = @root)
return nil if node.nil?

tree = { value: node.value }
tree[:left] = traverse(node.left)
tree[:right] = traverse(node.right)

tree
end

private

def update_parent_link(parent_node, current_node, new_node)
if parent_node.nil?
@root = new_node
elsif current_node.value < parent_node.value
parent_node.left = new_node
else
parent_node.right = new_node
end
end

def find_leftmost(node)
leftmost = node.left
leftmost_parent = node

while leftmost.left
leftmost_parent = leftmost
leftmost = leftmost.left
end

[leftmost, leftmost_parent]
end
end

RSpec.describe BinarySearchTree do
let(:bst) { BinarySearchTree.new }

before(:each) do
# Construct the following tree:
#
# 9
# 4 20
# 1 6 15 170
#
bst.insert(9)
bst.insert(4)
bst.insert(6)
bst.insert(20)
bst.insert(170)
bst.insert(15)
bst.insert(1)
end

describe "#insert" do
it "adds a value to the tree" do
expect(bst.traverse).to eq({ value: 9,
left: { value: 4,
left: { value: 1, left: nil, right: nil },
right: { value: 6, left: nil, right: nil } },
right: { value: 20, left: { value: 15, left: nil, right: nil },
right: { value: 170, left: nil, right: nil } } })
end
end

describe "#lookup" do
it "finds a value in the tree" do
node = bst.lookup(15)
expect(node.value).to eq(15)
end

it "returns nil for a value not in the tree" do
node = bst.lookup(100)
expect(node).to be_nil
end
end

describe "#remove" do
it "removes a leaf node" do
expect(bst.remove(1)).to be true

expect(bst.traverse).to eq({ value: 9,
left: { value: 4, left: nil,
right: { value: 6, left: nil, right: nil } },
right: { value: 20, left: { value: 15, left: nil, right: nil },
right: { value: 170, left: nil, right: nil } } })
end

it "removes a node with one child" do
expect(bst.remove(20)).to be true

expect(bst.traverse).to eq({ value: 9,
left: { value: 4, left: { value: 1, left: nil, right: nil },
right: { value: 6, left: nil, right: nil } },
right: { value: 170, left: { value: 15, left: nil, right: nil },
right: nil } })
end

it "removes a node with two children" do
expect(bst.remove(4)).to be true

expect(bst.traverse).to eq({ value: 9,
left: { value: 6, left: { value: 1, left: nil, right: nil },
right: nil },
right: { value: 20, left: { value: 15, left: nil, right: nil },
right: { value: 170, left: nil, right: nil } } })
end

it "returns false for a value not in the tree" do
expect(bst.remove(100)).to be false
end
end
end