Binary Search Tree Insertion method in Ruby - ruby

I'm working on data structures and I got stuck on one of the challenges. The objective is to insert array elements into a binary search tree based on their value i.e ( the root_node of the main tree is array[0], the left subtree's root_node is less than the parent node, and the right subtree's root_node is greater than the parent node). This is to be done recursively until all array elements are inserted into the BST.
I have implemented two classes:
That represents the node with attributes ( data, left, right):
class Node
attr_reader :data
attr_accessor :left, :right
def initialize(data)
#data = data
end
end
The BST class to represent the binary search tree with the root value set to nil:
class BST
attr_accessor :root
def initialize
#root = nil
end
def insert(node)
insert_node(#root, node)
end
def pre_order(node = #root)
return '' if node.nil?
print "#{node.data} "
pre_order(node.left)
pre_order(node.right)
end
private
def insert_node(node, element)
if node.nil?
node = element
elsif node.data > element.data
node.left = insert_node(node.left, element)
else
node.right = insert_node(node.right, element)
end
node
end
end
The insert_node is a private method for BST which does the actual work to insert a node to the tree. I separated it from insert because of the requirements for the expected solution which gets evaluated using RSpec.
I then did a pre_order traversal to print each Node to the terminal window.
I have a binary_search_tree method which accepts an array as input and calls the insert method on each array element. It's main functionality is to convert an array to a binary tree and print out all the tree nodes in pre_order format.
def binary_search_tree(array)
tree = BST.new
array.each { |e| tree.insert(Node.new(e)) }
tree.pre_order
end
If I run the binary_search_tree method with [8, 3, 10, 1, 6, 14, 4, 7, 13] as an argument, I expect to get the nodes printed out in the format # => "8 3 1 6 4 7 10 14 13" but nothing is happening and I don't know where I might have gone wrong. I have been stuck on this challenge for hours today, if anyone could assist that will mean a lot. Thanks : )
sample input:
puts binary_search_tree([8, 3, 10, 1, 6, 14, 4, 7, 13])
expected output:
8 3 1 6 4 7 10 14 13
got:

Oh, I finally found a way around the challenge. I'm just gonna demonstrate how I did it for future reference:
So, instead of using two separate methods insert and insert_helper node, I decided to get rid of the redundant code and came up with one method to solve the challenge. Here's the BST class structure:
class BST
attr_accessor :root
def initialize
#root = nil
end
def insert(node, head = #root)
return #root = node if #root.nil?
return node if head.nil?
if node.data < head.data
head.left = insert(node, head.left)
elsif node.data > head.data
head.right = insert(node, head.right)
end
head
end
def pre_order(node = #root)
return '' if node.nil?
result = ''
result += "#{node.data} "
result += pre_order(node.left)
result += pre_order(node.right)
end
end
The insert method now accepts two parameters node and ( head which is an optional parameter ), to allow us to perform recursive operations on the subtree nodes and yield the desired result.
pre_order prints each node's data, this is done in the recursive approach so each node in the Binary Search Tree gets printed out in pre_order format.
Now if you call the BST pre_order method for example:
def binary_search_tree(array)
tree = BST.new
array.each { |e| tree.insert(Node.new(e)) }
tree.pre_order
end
puts binary_search_tree([8, 3, 10, 1, 6, 14, 4, 7, 13])
you get the result 8 3 1 6 4 7 10 14 13. By altering the pre_order method, you can print the tree nodes in post_order, inorder, e.t.c.
I hope this will be useful for the others. Happy coding!!

return '' if node.nil?
This is the stop condition of the recursive pre_order and it's what you return from binary_search_tree. You don't seem to have anything that would format your tree in your desired shape, "8 3 1 6 4 7 10 14 13". So add that logic and call it at the end of binary_search_tree method.

Related

Returning Level-1 Node In a Balanced Binary Search Tree Ruby?

I am recursively creating a balanced binary search tree in Ruby using a sorted array. However, I'm having trouble at the ending return value. Instead of all the nodes bubbling up, creating a tree and returning the base level 1 node, the very last node at the bottom of the tree is returned.
It doesn't seem like the nodes being created are linked together at all (printing the instantiated class using p list only returns the last node). How do I link the nodes together and return the level 1 root node?
Code:
class Node
include Comparable
attr_accessor :value, :left, :right
def initialize(value, left = nil, right = nil)
#value = value
#left = left
#right = right
end
end
class Tree
attr_accessor :sorted_arr, :arr
def initialize(arr)
#arr = arr
#sorted_arr = arr.sort.uniq
end
#Problem: nodes not being linked together
def build_tree(arr, start, last)
if start > last
return nil
end
mid_index = (start + last) / 2
#root = Node.new(arr[mid_index])
#root.left = build_tree(arr, start, mid_index - 1)
#root.right = build_tree(arr, mid_index + 1, last)
return #root
end
end
list = Tree.new([1, 7, 4, 23, 8, 9, 4, 3, 6, 7, 9, 67, 6345, 324])
list.build_tree(list.sorted_arr, 0, list.sorted_arr.length-1)
p list
You should use root instead of #root.
Those starting with # are instance variables, so when you're calling to:
#root.left = build_tree(arr, start, mid_index - 1)
inside of that build_tree call, eventually you'll call also to #root = Node.new(arr[mid_index]) which it will replace the value already set in the parent call.

What's a proper algorithm to count all possible paths from any given vertex to another in a graph?

I'm working with graphs and I'd like to count all possible paths from given vertex X to given vertex Y.
This is the algorithm I've come up with:
class Graph
def paths_from(vertex_a, vertex_b, count = 0, visited = Array.new(#vertices.length, false))
return count if #vertices.none?(vertex_b) || #vertices.none?(vertex_a)
count += 1 if vertex_a == vertex_b
visited[#vertices.index(vertex_a)] = true
#net[#vertices.index(vertex_a)].each do |vertex|
paths_from(vertex, vertex_b, count, visited) unless visited[#vertices.index(vertex)]
end
count
end
end
Using recursion, I'm expecting to traverse df through the graph. However, I keep getting 0 instead of the expected value given below graph:
describe Graph do
context 'can output all possible from vertex a to vertex b.' do
let(:subject) { Graph.new(%w[a b c d e]) }
before(:each) do
subject.add_edge(0, 1)
subject.add_edge(0, 2)
subject.add_edge(0, 4)
subject.add_edge(1, 2)
subject.add_edge(1, 4)
subject.add_edge(2, 3)
subject.add_edge(3, 1)
end
it 'example #1' do
expect(subject.paths_from('a', 'f')).to eql 0 # => should output 0 and it does.
end
it 'example #2' do
expect(subject.paths_from('f', 'a')).to eql 0 # => should ouput 0 and it does.
end
it 'example #3' do
expect(subject.paths_from('a', 'b')).to eql 2
end
end
end
Doubt #1: I've checked geeksforgeeks approach tips regarding the algorithm: it states I should backtrack. What is that and how may I do it? I guess they're referencing the visited variable... but I've got no clue as to how to do that.
I'll drop the class definition just in case.
class Graph
attr_accessor :net, :vertices
def initialize(vertices = [])
#net = Array.new(vertices.length) { [] }
#vertices = vertices
end
def add_edge(vertex_a, vertex_b)
return if #net[vertex_a].nil? || #vertices[vertex_b].nil?
#net[vertex_a] << #vertices[vertex_b]
end
end
Doubt #2: If I print the count variable right before the #net loop, it prints 0, then '1' four times, yet it returns 0. Why is that? I suppose it's because it's returing #paths_from's first call... if that's the case, how may I return #paths_from's last call's count variable?
I assume the graph is directed and contains no cycles.
Suppose the graph is described by the following hash.
graph = { :A=>[:C, :D], :B=> [:D], :C=>[:D, :E, :F], :D=>[:G], :E=>[:F, :H],
:F=>[:D, :G, :I, :H], :G=>[:H, :I], :H=>[], :I=>[] }
The nodes are keys and the arcs are given by the keys and each element of a keys value. There are, for example, arcs :A->:C, :A-->:D, :B->:D and so on.
We can display this graph as follows.
Given two of the nodes, designated as the origin and terminus, the problem is to determine the number of paths from origin to terminus.
Suppose
origin = :A
terminus = :H
It is seen that there are nine paths from A to H:
A-C-E-H
A-C-E-F-H
A-C-E-F-G-H
A-C-E-F-D-G-H
A-C-F-H
A-C-F-D-G-H
A-C-F-G-H
A-C-D-G-H
A-D-G-H
I will give two solutions. The first is a recursion that requires little code but enumerates all paths. The number of such paths, however, can grow exponentially with the number of nodes. The second is more complex but is much faster for larger graphs. It's computational complexity appears to be only O(n2), where n is the number of nodes.
Enumerate paths from origin to destination
def tot_paths(graph, terminus, node)
graph[node].reduce(0) do |tot, n|
tot + ((n == terminus) ? 1 : tot_paths(graph, terminus, n))
end
end
tot_paths(graph, :H, :A)
#=> 9
More complex, but a much more efficient solution
The second approach requires two steps. The first is to perform a topological sort of the nodes of the graph.
Any array sorted that is an array of topologically-sorted nodes has the property that, for any pair of nodes ni = sorted[i] and nj = sorted[j] there is no path from nj to ni if j > i. A directed graph with no cycles is guaranteed to have at least one topological sort of nodes.
I have used Kuhn's algorithm (described at the above link) to produce the topological sort given by the following array:
[:B, :A, :C, :E, :F, :D, :G, :I, :H]
As shown below, if these nodes are viewed as being on a line, all arcs are directed from left to right. (For now disregard the numbers shown above the nodes.)
My implementation of the Kuhn algorithm is as follows.
nodes = graph.keys
#=> [:A, :B, :C, :D, :E, :F, :G, :H, :I]
incoming = graph.each_with_object(nodes.map { |n| [n, []] }.to_h) do |(k,v),h|
v.each { |n| h[n] << k }
end
#=> {:A=>[], :B=>[], :C=>[:A], :D=>[:A, :B, :C, :F], :E=>[:C], :F=>[:C, :E],
# :G=>[:D, :F], :H=>[:E, :F, :G], :I=>[:F, :G]}
incoming[:H] #=> [:E, :F, :G], for example, shows that the arcs directed into node :H are :E->:H, :F->:H and :G->:H.
no_incoming_nodes = incoming.select { |k,v| v.empty? }.keys
#=> [:A, :B]
sorted_nodes = []
until no_incoming_nodes.empty?
n = no_incoming_nodes.pop
sorted_nodes << n
graph[n].each do |next_node|
incoming[next_node].delete(n)
no_incoming_nodes << next_node if incoming[next_node].empty?
end
end
sorted_nodes
#=> [:B, :A, :C, :E, :F, :D, :G, :I, :H]
The second step is to implement a dynamic-programming algorithm to count the number of paths from node :A to node :H. I will explain how it works by explaining the meaning of the numbers above each node in the diagram immediatally above.
The number of paths from node I (the element of sorted_nodes that is followed by the terminus) is 0 (the number above I) because I is not the terminus and has no outgoing nodes.
Going back one node in sorted_nodes, the number of paths from G to the terminus is 1 as it is followed by the terminus (1) and node I, which has 0 paths to the terminus.
The number of paths from node D to the terminus is 1 because D is followed by only one node, G, and G has 1 path to the terminus.
Node F has 3 paths to the terminus, 1 that goes directly to the terminus, 1 that passes through D, 0 that pass through I and 1 that passes through G.
Similarly, there are 4 paths from node E to the terminus, 8 paths from node C to the terminus and, our objective, 9 paths from A, the origin, to the terminus.
The computation can be implemented as follows (using sorted_nodes computed earlier).
origin = :A
terminus = :H
tot_to = graph.each_with_object({}) do |(k,v),h|
(h[k] = k == terminus ? 1 : 0) if v.empty?
end
#=> {:H=>1, :I=>0}
(sorted_nodes.index(terminus) - 1).downto(sorted_nodes.index(origin)).each do |i|
n = sorted_nodes[i]
tot_to[n] = graph[n].sum { |m| tot_to[m] }
end
tot_to[origin]
#=> 9
Lastly, I would like to mention that the dynamic programming algorithm could have been organised differently, with roughly equal computational efficiency. Rather than beginning at the terminus and working backward, we could have started at the origin and worked forward until the terminus is reached, at each node computing the number of paths from A to the given node.
You don't appear to be recording the output of the recursion.
can you try something like:
count += paths_from(vertex, vertex_b, count, visited) unless visited[#vertices.index(vertex)]
not passing in a count at all:
class Graph
attr_accessor :net, :vertices, :visited
def initialize(vertices = [])
#net = Array.new(vertices.length) { [] }
#visited = Array.new(vertices.length, false)
#vertices = vertices
end
def add_edge(vertex_a, vertex_b)
return if #net[vertex_a].nil? || #vertices[vertex_b].nil?
#net[vertex_a] << #vertices[vertex_b]
end
def paths_from(vertex_a, vertex_b)
return 0 if #vertices.none?(vertex_b) || #vertices.none?(vertex_a)
count = 0
count += 1 if vertex_a == vertex_b
#visited[#vertices.index(vertex_a)] = true
#net[#vertices.index(vertex_a)].each do |vertex|
count += paths_from(vertex, vertex_b) unless #visited[#vertices.index(vertex)]
end
count
end
end

Find Leaves of Binary Tree

Working on following problem:
Given a binary tree, collect a tree's nodes as if you were doing this: Collect and remove all leaves, repeat until the tree is empty.
Example:
Given binary tree
1
/ \
2 3
/ \
4 5
Returns [4, 5, 3], [2], [1].
Explanation:
1. Removing the leaves [4, 5, 3] would result in this tree:
1
/
2
2. Now removing the leaf [2] would result in this tree:
1
3. Now removing the leaf [1] would result in the empty tree:
[]
Returns [4, 5, 3], [2], [1].
My idea was a simple recursive algorithm shown below. The idea is to find the leaves of the left subtree and the right subtree, and weave them such that the depths are in the right subarray. I've tested the 'weave' method pretty thoroughly, and I think it's fine. My concern is with my recursive implementation-- I'm getting an answer way off from the correct one, and not sure why.
Below is my code with sample input/output:
def find_leaves(root)
return [] if root.nil?
#create leaf_arr of root.left and root.right
#weave them in order.
#add the root
left_arr = find_leaves(root.left)
right_arr = find_leaves(root.right)
weave(left_arr, right_arr) << [root]
end
def weave(arr1, arr2) #these are 2d arrs
i = 0
until i == arr1.length || i == arr2.length #potential nil/empty case here
arr1[i] += arr2[i]
i += 1
end
if i < arr2.length
#either arr 1 or arr2 isn't finished. if arr1 isn't finished, we're done. if arr2 isnt finished, do the below:
until i == arr2.length
arr1 << arr2[i]
i += 1
end
end
arr1
end
Sample input/output/correct answer:
Run Code Result: ×
input: [1,2,3,4,5]
Your answer: [[[4],[5],[3]],[[2,4,5]],[[1,2,3,4,5]]]
Expected answer: [[4,5,3],[2],[1]]
I've printed the output for the left_arr and right_arr variables and they look fine, and I've stress-tested my weave algorithm. Am I off conceptually here?
I can't comment so I will do it like this. (do remember that i dont know ruby)
I think something goes already wrong in how the double arrays (root.left and root.right) are defined. How are they defined? how is root defined?
But the following eplains the repeat of the whole array.
weave(left_arr, right_arr) << [root]
This should be someting in the line of this.
weave(left_arr, right_arr) << [root.root]
Otherwise you are appending the whole root array wich is [1,2,3,4,5].
So this explains the adding of last part. [[[4],[5],[3]],[[2,4,5]],[[1,2,3,4,5]]].
My suggestion in finding the error in weave would be to print arr1 and arr2 at every stage....
Could you show that..
In your code you are using pure depth first search algorithm DFS and with that algorithm I think that you can hardly achieve your goal with array joggling you are doing in weave function. Because your tree will be processed in this order 4 , 5 , 2 , 3 , 1.
One solution will be to do it with iteration (pseudo code):
function doJob(root) begin
leaves = findLeaves(root)
while leaves.size > 0 do begin
for each leaf in leaves delete(leaf)
leaves = findLeaves(root)
end
delete(root)
end
function findLeaves(node) begin
if node = nil then begin
return []
end
else begin
leftLeaves = findLeaves(node.left)
rightLeaves = fingLeaves(node.right)
leaves = leftLeaves + rightLeaves
if leaves.size == 0 then begin
leaves.add(node)
end
return leaves
end
end
Since this still sits open and seems to fair highly when I google search your title. I'll show a pretty expressive solution:
def find_leaves(root)
return [] if root.nil?
return [[root.val]] if root.left.nil? && root.right.nil?
todo = [root]
leaves = []
until todo.empty?
top = todo.shift
%w[left right].each do |path|
leaf = top.send(path)
next if leaf.nil?
if leaf.left.nil? && leaf.right.nil?
leaves << leaf.val
top.instance_variable_set("##{path}", nil)
else
todo << leaf
end
end
end
[leaves].concat(find_leaves(root))
end
A more refactored version:
def find_leaves(root)
leaves = []
search = lambda do |branch|
return -1 unless branch
i = 1 + [search[branch.left], search[branch.right]].max
(leaves[i] ||= []) << branch.val
i
end
search[root]
leaves
end
They're both about the same speed, and really the first one is easier to read and understand.

Ruby lazy enumerator returning different object types depending on use

I was attempting to solve Project Euler #58 in a functional manner with ruby.
Briefly, I created an enumerator to return the corner number of each ring. I was then chaining functional operators on the enumerator. When I get my result, I find that it has a different class depending on how I use it.
spiral = Enumerator.new do |yielder|
n = 3
step = 2
loop do
vals = n.step(nil, step).take(4)
yielder.yield vals
step += 2
n = vals.last + step
end
end
primes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113]
levels = spiral
.lazy
.map { |ring| ring.count { |n| primes.include? n } }
.with_object({:total=>1.0, :primes=>0})
.take_while do |ring_primes, counts|
counts[:total] += 4
counts[:primes] += ring_primes
(counts[:primes] / counts[:total]) > 0.5
end
The class of levels is a lazy enumerator. I would expect it to contain the number of primes in each ring [3, 2, 3, etc.] - see the project euler reference.
If I just print from the enumerator, I get what I expect:
levels.each do |level|
puts "#{level}"
end
Returns:
3
2
3
1
But if I loop .with_index I get an array result back where the expected value is the first member and the second is my .with_object parameter
levels.each.with_index do |level, ix|
puts "#{ix}: #{level}"
end
Returns:
0: [3, {:total=>5.0, :primes=>3}]
1: [2, {:total=>9.0, :primes=>5}]
2: [3, {:total=>13.0, :primes=>8}]
3: [1, {:total=>17.0, :primes=>9}]
Why does the lazy enumerator work this way and how could I predict for it in the future?
Update
I asked around on the IRC ruby channel and no one there had any idea about it. They said they had discussed it a day or two ago and hadn't come to any conclusions.
In general, it seems one must just deal with it and move on.
What's happening here is you're conveniently ignoring the structure that's returned and plucking out the first item to display. In this case the first item is the counts structure you produce.
Have a look at this:
levels.each do |*level|
puts level.inspect
end
That shows you what's actually in the levels results. When Ruby calls a lambda it will discard any additional data that doesn't fit with the number of arguments the block accepts.
If you don't need that metadata, strip it out:
levels = spiral
.lazy
.map { |ring| ring.count { |n| primes.include? n } }
.with_object({:total=>1.0, :primes=>0})
.take_while do |ring_primes, counts|
counts[:total] += 4
counts[:primes] += ring_primes
(counts[:primes] / counts[:total]) > 0.5
end
.map { |r,_| r }
That removes the extraneous element in the results.
Here's a way of cleaning up your Enumerator a bit:
class Spiral
include Enumerable
def each
Enumerator.new do |yielder|
n = 3
step = 2
loop do
vals = n.step(nil, step).take(4)
yielder.yield vals
step += 2
n = vals.last + step
end
end
end
end
Then you can create one with:
Spiral.new.each ...

Depth First Search Efficiency

I have implemented a DFS method which takes in a search value and a Binary Search Tree as arguments. The method then searches the tree for the given value, returning it when found.
When I call the method, there appears to be a duplicate node visit which may be affecting the efficiency. Is this visit in fact a duplication or just a reflection of the nature of DFS?
For instance, if my binary tree looks like the one below and I'm looking for the the value 3, the search is popping the 5 node off the stack, then the 2, then the 1, then retrieving the 2 node from the stack again before finding the 3. Is this stack retrieval of the 2 duplicative? Is it a proper DFS?
5
/ \
/ \
2 7
/ \ / \
1 3 6 8
\ \
4 9
Binary Tree
class Node
attr_accessor :value, :left, :right
def initialize(value)
#value = value
end
end
def build_tree(array, *indices)
array.sort.uniq!
mid = (array.length-1)/2
first_element = indices[0]
last_element = indices[1]
if !first_element.nil? && first_element >last_element
return nil
end
root = Node.new(array[mid])
root.left = build_tree(array[0..mid-1], 0, mid-1)
root.right = build_tree(array[mid+1..-1], mid+1, array.length-1)
return root
end
Depth First Search Method
def depth_first_search(search_value, tree)
stack = [tree]
visited = [tree]
while !stack.empty?
current = stack.last
visited << current
puts current
p current
if current.value == search_value
puts current
exit
elsif !current.left.nil? && !visited.include?(current.left)
if current.left.value == search_value
puts current.left
exit
else
visited << current.left
stack << current.left
end
elsif !current.right.nil? && !visited.include?(current.right)
if current.right.value == search_value
puts current.right
exit
else
visited << current.right
stack << current.right
end
else
stack.pop
end
end
puts "nil"
end
Method Call
binary_tree = build_tree([1,2,3,4,5,6,7,8,9])
depth_first_search(3, binary_tree)
Now, since it is DFS, it works that way. DFS in a binary-tree works exactly like pre-order traversal of the tree. So, for the example tree in the figure, DFS would be visiting like:
5-2-1-(2)-3-4-(3)-(2)-(5)-7-6-(7)-8-9
Here, the values in brackets is the "second visit" that you are calling, but, it does not actually visit those nodes. So, it is alright.
Also, I'd recommend using a binary search if the input tree is BST (not DFS).

Resources