I've implemented Binary search tree in ruby, and now I need to clean whole structure but I can't find any way to do that.
def post_order_clean(node)
if node.left != nil
post_order_clean(node.left)
end
if node.right != nil
post_order_clean(node.right)
end
node = nil
end
But when i do sth like that:
example = [4,6,9,5,7,3,1]
tree = BST::BinaryTree.new(example)
tree.clean
puts tree.root.value
It still prints out 4 as a root value.
How can I clean the tree with post-order traversing method?
Edit:
Just like #Cary Swoveland mentioned:
.. successively remove nodes (together with arcs directed to the node) that have no arcs directed to other nodes.
That's my point.
I did this solution for insert and remove:
class TreeNode
attr_accessor :val, :left, :right
def initialize(val)
#val = val
#left, #right = nil, nil
end
def insert(v)
side = (v <= val ? :left : :right)
if send(side)
send(side).insert(v)
else
send("#{side}=", TreeNode.new(v))
end
end
def delete(v)
if v < val
self.left = self.left ? self.left.delete(v) : nil
elsif v > val
self.right = self.right ? self.right.delete(v) : nil
else
if self.left.nil?
return self.right
elsif self.right.nil?
return self.left
end
min = self.right
min = min.left while !min.left.nil?
self.val = min.val
self.right = self.right.delete(self.val)
end
self
end
end
Related
I was recently told that AVL sort is not in place. Can anyone please explain it? From the below code, I am not sure where I assign extra space when sorting. In this code, when a data structure is built or an element are inserted, elements are ordered by their key.
Reference for the claim: They are using this claim to motivate "binary heap"
[1].https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-spring-2020/lecture-notes/MIT6_006S20_r08.pdf
Reference for code:
[2]. https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-spring-2020/lecture-notes/MIT6_006S20_r06.pdf
[3]. https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-006-introduction-to-algorithms-spring-2020/lecture-notes/MIT6_006S20_r07.pdf
def height(A):
if A: return A.height
else: return -1
class Binary_Node:
def __init__(self, x):
self.item = x
self.parent = None
self.left = None
self.right = None
self.subtree_update()
def subtree_update(self):
self.height = 1 + max(height(self.left), height(self.right))
def subtree_iter(self):
if self.left: yield from self.left.subtree_iter()
yield self
if self.right: yield from self.right.subtree_iter()
def subtree_first(self):
if self.left: return self.left.subtree_first()
else: return self
def subtree_last(self):
if self.right: return self.right.subtree_last()
else: return self
def sucessor(self):
if self.right: return self.right.subtree_first()
while self.parent and (self is self.parent.right): #A is parent's left child and A's parent exists
self = self.parent
return self.parent
def predecessor(self):
if self.left: return self.left.subtree_last()
while self.parent and (self is self.parent.left):
self = self.parent
return self.parent
def subtree_insert_before(self, A):
if self.left:
self = self.left.subtree_last()
self.right, A.parent = A, self
else:
self.left, A.parent = A, self
self.maintain()
def subtree_insert_after(self, A):
if self.right:
self = self.right.subtree_first()
self.left, A.parent = A, self
else:
self.right, A.parent = A, self
self.maintain()
def delete(self):
if not self.left and not self.right: # when self is leaf
if self.parent:
A = self.parent
if A.left is self: A.left = None
else: A.right = None
self.parent = None
if self.left:
self.item, self.left.subtree_last().item = self.left.subtree_last().item, self.item
self.left.subtree_last().delete()
else:
self.item, self.right.subtree_first().item = self.right.subtree_first().item, self.item
self.right.subtree_last().delete()
def subtree_delete(self):
if self.left or self.right:
if self.left: B = self.predecessor()
else: B = self.sucessor()
self.item, B.item = B.item, self.item
return B.subtree_delete()
if self.parent:
if self.parent.left is self: self.parent.left = None
else: self.parent.right = None
self.parent.maintain()
return self
def subtree_rotate_right(self):
assert self.left
B, E = self.left, self.right
A, C = B.left, B.right
B, self = self, B
self.item, B.item = B.item, self.item
B.left, B.right = A, self
self.left, self.right = C, E
if A: A.parent = B
if E: E.parent = self
B.subtree_update()
self.subtree_update()
def subtree_rotate_left(self):
assert self.right
A, D = self.left, self.right
C, E = D.left, D.right
self, D = D, self
self.item, D.item = D.item, self.item
self.left, self.right = A, C
D.left, D.right = self, E
if A: A.parent = self
if E: E.parent = D
self.subtree_update()
D.subtree_update()
def skew(self):
return height(self.right) - height(self.left)
def rebalance(self):
if self.skew() == 2:
if self.right.skew() < 0:
self.right.subtree_rotate_right()
self.subtree_rotate_left()
elif self.skew() == -2:
if self.left.skew() > 0:
self.left.subtree_rotate_left()
self.subtree_rotate_right()
def maintain(self):
self.rebalance()
self.subtree_update()
if self.parent: self.parent.maintain()
class Binary_Tree:
def __init__(self, Node_Type = Binary_Node):
self.root = None
self.size = 0
self.Node_Type = Node_Type
def __len__(self): return self.size
def __iter__(self):
if self.root:
for A in self.root.subtree_iter():
yield A.item
def build(self, X):
A = [x for x in X]
def build_subtree(A, i, j):
c = (i + j) // 2
root = self.Node_Type(A[c])
if i < c:
root.left = build_subtree(A, i, c - 1)
root.left.parent = root
if j > c:
root.right = build_subtree(A, c + 1, j)
root.right.parent = root
return root
self.root = build_subtree(A, 0, len(A) - 1)
class BST_Node(Binary_Node):
def subtree_find(self, k):
if self.item.key > k:
if self.left: self.left.subtree_find(k)
elif self.item.key < k:
if self.right: self.right.subtree_find(k)
else: return self
return None
def subtree_find_next(self, k):
if self.item.key <= k:
if self.right: return self.right.subtree_find_next(k)
else: return None
elif self.item.key > k:
if self.left: return self.left.subtree_find_next(k)
else: return self
return self
def subtree_find_prev(self, k):
if self.item.key >= k:
if self.left: return self.left.subtree_find_prev(k)
else: return None
elif self.item.key < k:
if self.right: return self.right.subtree_find_prev(k)
else: return self
return self
def subtree_insert(self, B):
if B.item.key < self.item.key:
if self.left: self.left.subtree_insert(B)
else: self.subtree_insert_before(B)
elif B.item.key > self.item.key:
if self.right: self.right.subtree_insert(B)
else: self.subtree_insert_after(B)
else:
self.item = B.item
class Set_Binary_Tree(Binary_Tree):
def __init__(self): super().__init__(BST_Node)
def iter_order(self): yield from self
def build(self, X):
for x in X: self.insert(x)
def find_min(self):
if self.root: return self.root.subtree_first()
def find_max(self):
if self.root: return self.root.subtree_last()
def find(self, k):
if self.root:
node = self.root.subtree_find(k)
if node:
return node.item
def find_next(self, k):
if self.root:
node = self.root.subtree_find_next(k)
if node:
return node.item
def find_prev(self, k):
if self.root:
node = self.root.subtree_find_prev(k)
if node:
return node.item
def insert(self, x):
new = self.Node_Type(x)
if self.root:
self.root.subtree_insert(new)
if new.parent is None: return False
else:
self.root = new
self.size += 1
return True
def delete(self, k):
assert self.root
node = self.root.subtree_find(k)
assert node
ext = node.subtree_delete()
if ext.parent is None: self.root = None
self.size -= 1
return ext.item
Wikipedia defines an in-place algorithm as follows:
In computer science, an in-place algorithm is an algorithm which transforms input using no auxiliary data structure. However, a small amount of extra storage space is allowed for auxiliary variables. The input is usually overwritten by the output as the algorithm executes. An in-place algorithm updates its input sequence only through replacement or swapping of elements.
So one of the properties of an algorithm that is called "in-place" is that it does not copy all input values into an newly allocated data structure. If an algorithm creates a binary search tree (like AVL), for which node objects are created that are populated with the input values, then it cannot be called in-place by the above definition, even if at the end of the process the values are copied back into the input array.
As a comparison, heap sort does not have to create a new data structure, as the input array can be used to reorganise its values into a heap. It merely has to swap values in that array in order to sort it. It is therefore an in-place algorithm.
I am trying to make a LinkedList assignment on ruby and was facing difficulties to do the remove method, so I searched in google and got the code from the remove method.
What I can't understand is the logic behind it, I can't understand how it works, how it works the logic to delete it.
class Node
attr_accessor :value, :next_node
def initialize(value, next_node = nil)
#value = value
#next_node = next_node
end
end
class LinkedList
def add(number)
new_node = Node.new(number)
if #head.nil?
#head = new_node
#tail = new_node
else
#tail.next_node = new_node
#tail = new_node
end
end
def get(index)
#your code here
node = #head
while index > 0 && node
node = node.next_node
index -= 1
end
node.value
end
def add_at (index, number)
if #head.nil?
#head = Node.new(number)
#tail = Node.new(number)
else
first = #head
index -= 1
while index > 0 && first
first = first.next_node
index -= 1
end
old_next = first.next_node
first.next_node = Node.new(number)
first.next_node.next_node = old_next
end
end
def remove (index)
node = #head
index -= 1
while index > 0 && node
node = node.next_node
index -= 1
end
node.next_node = node.next_node.next_node
end
end
In the mutation example below, I don't understand how the linked list is reversed.
class LinkedListNode
attr_accessor :value, :next_node
def initialize(value, next_node=nil)
#value = value
#next_node = next_node
end
end
def print_values(list_node)
print "#{list_node.value} --> "
if list_node.next_node.nil?
print "nil\n"
return
else
print_values(list_node.next_node)
end
end
def reverse_list(list, previous=nil)
current_head = list.next_node
list.next_node = previous
if current_head
reverse_list(current_head, list)
else
list
end
end
node1 = LinkedListNode.new(37)
node2 = LinkedListNode.new(99, node1)
node3 = LinkedListNode.new(12, node2)
print_values(node3)
puts "-------"
revlist = reverse_list(node3)
print_values(revlist)
If I just return current_head, I get 99->37->nil, which makes sense because 99 would be next_node. Returning the next line,
list.next_node = previous
throws an error because print_values method can't print the value for nil. I'm not understanding what is reversing the list. If anyone could explain this to me, I would appreciate it.
Here's a little visualization I made up.
^ points to head of the list. At each level of recursion, its right arrow is "turned" to point from element on the right to element on the left. Proceed until there is a right arrow (pointing to a non-nil). If right arrow points to nil, return the current head.
previous
↓
nil 12 -> 99 -> 37 -> nil
^
previous
↓
nil <- 12 99 -> 37 -> nil
^
previous
↓
nil <- 12 <- 99 37 -> nil
^
nil <- 12 <- 99 <- 37
^
# Create a LinkedListNode instance with value 36
node1 = LinkedListNode.new(37)
# Create a LinkedListNode instance which next value is node1
node2 = LinkedListNode.new(99, node1)
# node2 -> node1
# Create a LinkedListNode instance which next value is node2
node3 = LinkedListNode.new(12, node2)
# node3 -> node2 -> node1
print_values(node3)
# 12 -> 99 -> 37
Fist pass into reverse_list method
reverse_list(node3)
def reverse_list(list, previous=nil)
# set current_head to node2
current_head = list.next_node
# Change node3.next_node node2-->nil
list.next_node = previous
if current_head
# reverse_list(node2, node3)
reverse_list(current_head, list)
else
list
end
end
Second pass into reverse_list method
reverse_list(node2, node3)
def reverse_list(list, previous=nil)
# set current_head to node1
current_head = list.next_node
# Change node2.next_node node1-->node3
list.next_node = previous
if current_head
# reverse_list(node1, node2)
reverse_list(current_head, list)
else
list
end
end
last pass into reverse_list method
reverse_list(node1, node2)
def reverse_list(list, previous=nil)
# set current_head to nil
current_head = list.next_node
# Change node1.next_node nil-->node2
list.next_node = previous
if current_head
reverse_list(current_head, list)
else
# The end, return node1
list
end
end
By the way linked list is not a common practice in the ruby languages (and all the languages with a garbage collector), there is generally a class (such as Array for example) which have all the functionalities and flexibility you may need.
Here is a simple solution if someone is looking to do this without recursion
class Node
attr_accessor :value, :next
def initialize(value, next_node)
#value = value
#next = next_node
end
end
class LinkedList
attr_accessor :head, :tail
def add(value)
if(#head.nil?)
#head = Node.new(value, nil)
#tail = #head
else
#tail.next = Node.new(value, nil)
#tail = #tail.next
end
end
def reverse(list)
return nil if list.nil?
prev = nil
curr = list.head
while(curr != nil)
temp = curr.next
curr.next = prev
prev = curr
curr = temp
end
list.head = prev
list
end
def display(list)
return nil if list.nil?
curr = list.head
arr = []
while(curr != nil)
arr.push(curr.value)
curr = curr.next
end
p arr
end
end
list = LinkedList.new()
list.add(1)
list.add(2)
list.add(3)
list.display(list) #list before reversing [1,2,3]
list.display(list.reverse(list)) #list after reversing [3,2,1]
I've been trying to implement BinaryTree class in Ruby, but I'm getting the stack level too deep error, although I don't seem to be using any recursion in that particular piece of code:
1. class BinaryTree
2. include Enumerable
3.
4. attr_accessor :value
5.
6. def initialize( value = nil )
7. #value = value
8. #left = BinaryTree.new # stack level too deep here
9. #right = BinaryTree.new # and here
10. end
11.
12. def empty?
13. ( self.value == nil ) ? true : false
14. end
15.
16. def <<( value )
17. return self.value = value if self.empty?
18.
19. test = self.value <=> value
20. case test
21. when -1, 0
22. self.right << value
23. when 1
24. self.left << value
25. end
26. end # <<
27.
28. end
Edit: My question has gone a little bit off track. The current code setting gives me the stack level too deep error at line 8. However, if I employ Ed S.'s solution
#left = #right = nil
then the << method complains saying: undefined method '<<' for nil:NilClass (NoMethodError) at line 22.
Could anyone suggest how to resolve this? My idea is that if I could somehow tell the BinaryTree class that variables left and right are of instances of BinaryTree (i.e. their type is BinaryTree) it would all be well. Am I wrong?
although I don't seem to be using any recursion in that particular piece of code:
Yet...
def initialize( value = nil )
#value = value
#left = BinaryTree.new # this calls initialize again
#right = BinaryTree.new # and so does this, but you never get there
end
That is infinite recursion. You call initilize, which in turn calls new, which in turn calls initialize... and around we go.
You need to add a guard in there to detect that you have already initialized the main node and are now initializing leafs, in which case, #left and #right should just be set to nil.
def initialize( value=nil, is_sub_node=false )
#value = value
#left = is_sub_node ? nil : BinaryTree.new(nil, true)
#right = is_sub_node ? nil : BinaryTree.new(nil, true)
end
To be honest though... why aren't you just initializing left and right to nil to begin with? They don't have values yet, so what are you gaining? It makes more sense semantically; you create a new list with one element, i.e., elements left and right don't yet exist. I would just use:
def initialize(value=nil)
#value = value
#left = #right = nil
end
1. class BinaryTree
2. include Enumerable
3.
4. attr_accessor :value
5.
6. def initialize( value = nil )
7. #value = value
8. end
9.
10. def each # visit
11. return if self.nil?
12.
13. yield self.value
14. self.left.each( &block ) if self.left
15. self.right.each( &block ) if self.right
16. end
17.
18. def empty?
19. # code here
20. end
21.
22. def <<( value ) # insert
23. return self.value = value if self.value == nil
24.
25. test = self.value <=> value
26. case test
27. when -1, 0
28. #right = BinaryTree.new if self.value == nil
29. self.right << value
30. when 1
31. #left = BinaryTree.new if self.value == nil
32. self.left << value
33. end
34. end # <<
35. end
You might need to fix the infinite recursion in your code. Here's a working example of a binary tree. You need to have a base condition to terminate your recursion somewhere, else it'll be a stack of infinite depth.
Example of Self-Referential Data Structures - A Binary Tree
class TreeNode
attr_accessor :value, :left, :right
# The Tree node contains a value, and a pointer to two children - left and right
# Values lesser than this node will be inserted on its left
# Values greater than it will be inserted on its right
def initialize val, left, right
#value = val
#left = left
#right = right
end
end
class BinarySearchTree
# Initialize the Root Node
def initialize val
puts "Initializing with: " + val.to_s
#root = TreeNode.new(val, nil, nil)
end
# Pre-Order Traversal
def preOrderTraversal(node = #root)
return if (node == nil)
preOrderTraversal(node.left)
preOrderTraversal(node.right)
puts node.value.to_s
end
# Post-Order Traversal
def postOrderTraversal(node = #root)
return if (node == nil)
puts node.value.to_s
postOrderTraversal(node.left)
postOrderTraversal(node.right)
end
# In-Order Traversal : Displays the final output in sorted order
# Display smaller children first (by going left)
# Then display the value in the current node
# Then display the larger children on the right
def inOrderTraversal(node = #root)
return if (node == nil)
inOrderTraversal(node.left)
puts node.value.to_s
inOrderTraversal(node.right)
end
# Inserting a value
# When value > current node, go towards the right
# when value < current node, go towards the left
# when you hit a nil node, it means, the new node should be created there
# Duplicate values are not inserted in the tree
def insert(value)
puts "Inserting :" + value.to_s
current_node = #root
while nil != current_node
if (value < current_node.value) && (current_node.left == nil)
current_node.left = TreeNode.new(value, nil, nil)
elsif (value > current_node.value) && (current_node.right == nil)
current_node.right = TreeNode.new(value, nil, nil)
elsif (value < current_node.value)
current_node = current_node.left
elsif (value > current_node.value)
current_node = current_node.right
else
return
end
end
end
end
bst = BinarySearchTree.new(10)
bst.insert(11)
bst.insert(9)
bst.insert(5)
bst.insert(7)
bst.insert(18)
bst.insert(17)
# Demonstrating Different Kinds of Traversals
puts "In-Order Traversal:"
bst.inOrderTraversal
puts "Pre-Order Traversal:"
bst.preOrderTraversal
puts "Post-Order Traversal:"
bst.postOrderTraversal
=begin
Output :
Initializing with: 10
Inserting :11
Inserting :9
Inserting :5
Inserting :7
Inserting :18
Inserting :17
In-Order Traversal:
5
7
9
10
11
17
18
Pre-Order Traversal:
7
5
9
17
18
11
10
Post-Order Traversal:
10
9
5
7
11
18
17
=end
Ref: http://www.thelearningpoint.net/computer-science/basic-data-structures-in-ruby---binary-search-tre
#pranshantb1984 - The ref you gave is good one but I think there is a small change in code. Need to update PreOrder and PostOrder code as given below
# Post-Order Traversal
def postOrderTraversal(node= #root)
return if (node == nil)
postOrderTraversal(node.left)
postOrderTraversal(node.right)
puts node.value.to_s
end
# Pre-Order Traversal
def preOrderTraversal(node = #root)
return if (node == nil)
puts node.value.to_s
preOrderTraversal(node.left)
preOrderTraversal(node.right)
end
Pre Order Traversal
10 -> 9 -> 5 -> 7 -> 11 -> 18 -> 17
Post Order Traversal
7 -> 5 -> 9 -> 17 -> 18 -> 11 -> 10
I created a singly LinkedList class with ruby.
Everything went well till trying to reverse the linked list.
It does not reverse linkedlist by this method, but when I add
#head.next = nil
after left_tmp = #head in reverse method, it just works fine.
I couldn't figure out why it works when I add that, does anyone have the explanation?
BTW I am fairly new to ruby, so please don't hesitate to tell me if there are some other things that are not "Good Practice in Ruby".
Here is classes and relevant methods:
class LlNode
attr_reader :data
attr_accessor :next
def initialize(val=nil)
#data = val
#next = nil
end
def to_s
"node_data=#{#data}"
end
end
class LinkedList
def initialize
#list = []
#head = LlNode.new
end
def insert(val)
n = LlNode.new val
# List is empty
if is_empty?
#head = n
else
n.next = #head
#head = n
end
self
end
def reverse
return if is_empty? or #head.next.nil?
curr = #head.next
right_tmp = curr.next
left_tmp = #head
while curr != nil
curr.next = left_tmp
left_tmp = curr
curr = right_tmp
right_tmp = right_tmp.next unless right_tmp.nil?
end
#head = left_tmp
end
end
When you're reversing a linked list, the first node becomes the last. In a singly-linked list, the last node's next pointer points to null. #head, which is initially your first node becomes the last. That's why you add the #head.next = nil.
Edit: Simulating a dry-run to better explain the problem
Assume two nodes in the linked list: 1->2
curr = #head.next (2)
right_tmp = curr.next (nil)
left_tmp = #head (1)
First iteration of the while loop:
curr.next = left_tmp ( 1 <-> 2)
left_tmp = curr (2)
curr = right_tmp (nil)
right_tmp = right_tmp.next unless right_tmp.nil? (nil)
There is no second iteration since curr == nil
Now:
#head = left_tmp (#head points to '2')
Final linked list state is:
1 <-> 2