ruby-avl 0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/Gemfile +3 -0
- data/README.md +0 -0
- data/Rakefile +7 -0
- data/lib/ruby-avl.rb +6 -0
- data/lib/ruby-avl/avl_tree.rb +91 -0
- data/lib/ruby-avl/bs_tree.rb +117 -0
- data/lib/ruby-avl/bs_tree_traversal.rb +72 -0
- data/lib/ruby-avl/node.rb +11 -0
- data/lib/ruby-avl/version.rb +3 -0
- data/ruby-avl.gemspec +20 -0
- data/spec/avl_tree_spec.rb +121 -0
- data/spec/bs_tree_spec.rb +95 -0
- data/spec/bs_tree_traversal_spec.rb +68 -0
- data/spec/node_spec.rb +12 -0
- data/spec/spec_helper.rb +28 -0
- metadata +101 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7a02458c007e726e16b423af5bec30aabd799260
|
4
|
+
data.tar.gz: e0e82835afd930aff2858a6083b2357c084d300e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3fd204050d57a8f9918d2487bec619d82043352f0052f71bc75932db3e869ef2e959b02ec094b8252c14986a69c321b23403f5c99634f0f0ac0f012fb3388f4c
|
7
|
+
data.tar.gz: d45ac33cc6aa5850cfe309873000c36b81b66f0f6ef94860988e9e8e4b6e650bd5331c8f6593b10fbabbf9ab107b3eddb6fd788c7866873a49b112c338a2ac5e
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Gemfile.lock
|
data/Gemfile
ADDED
data/README.md
ADDED
File without changes
|
data/Rakefile
ADDED
data/lib/ruby-avl.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
require_relative 'bs_tree'
|
2
|
+
module AVLTree
|
3
|
+
class AVLTree < BSTree::BSTree
|
4
|
+
|
5
|
+
# Public interfaces implemented in superclass
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
# Calls superclass (BSTree) method passing in parameters
|
10
|
+
# recieved from caller. Passes in the returned value of
|
11
|
+
# BSTree#add_to_tree to :rebalance and returns final result.
|
12
|
+
def add_to_tree(item, node)
|
13
|
+
return rebalance(super(item, node))
|
14
|
+
end
|
15
|
+
|
16
|
+
# Calls superclass (BSTree) method passing in parameters
|
17
|
+
# recieved from caller. Passes in the returned value of
|
18
|
+
# BSTree#remove_from_tree to :rebalance and returns final
|
19
|
+
# result.
|
20
|
+
def remove_from_tree(item, node)
|
21
|
+
return rebalance(super(item, node))
|
22
|
+
end
|
23
|
+
|
24
|
+
# Checks the balance factor of the node and rotates the tree
|
25
|
+
# if inbalanced in any way. Balance algorithm:
|
26
|
+
# IF tree is right heavy
|
27
|
+
# IF tree's right subtree is left heavy (Right Left Case)
|
28
|
+
# Perform Double Left rotation
|
29
|
+
# ELSE (Right Right Case)
|
30
|
+
# Perform Single Left rotation
|
31
|
+
# ELSE IF tree is left heavy
|
32
|
+
# IF tree's left subtree is right heavy (Left Right Case)
|
33
|
+
# Perform Double Right rotation
|
34
|
+
# ELSE (Left Left Case)
|
35
|
+
# Perform Single Right rotation
|
36
|
+
# END IF
|
37
|
+
def rebalance(node)
|
38
|
+
if balance_factor(node) >= 2
|
39
|
+
if (balance_factor(node.left) < 0)
|
40
|
+
return double_rotate_right(node)
|
41
|
+
else
|
42
|
+
return rotate_right(node)
|
43
|
+
end
|
44
|
+
elsif balance_factor(node) <= -2
|
45
|
+
if (balance_factor(node.right) > 0)
|
46
|
+
return double_rotate_left(node)
|
47
|
+
else
|
48
|
+
return rotate_left(node)
|
49
|
+
end
|
50
|
+
else
|
51
|
+
return node
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Node becomes the left subtree of it's right subtree.
|
56
|
+
def rotate_left(node)
|
57
|
+
new_node = node.right
|
58
|
+
node.right = new_node.left
|
59
|
+
new_node.left = node
|
60
|
+
return new_node
|
61
|
+
end
|
62
|
+
|
63
|
+
# Right rotation performed on node's right subtree before
|
64
|
+
# node is rotated left.
|
65
|
+
def double_rotate_left(node)
|
66
|
+
node.right = rotate_right(node.right)
|
67
|
+
return rotate_left(node)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Node becomes the right subtree of it's left subtree.
|
71
|
+
def rotate_right(node)
|
72
|
+
new_node = node.left
|
73
|
+
node.left = new_node.right
|
74
|
+
new_node.right = node
|
75
|
+
return new_node
|
76
|
+
end
|
77
|
+
|
78
|
+
# Left rotation performed on node's left subtree before
|
79
|
+
# node is rotated right.
|
80
|
+
def double_rotate_right(node)
|
81
|
+
node.left = rotate_left(node.left)
|
82
|
+
return rotate_right(node)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Calculates the height of the left subtree, then subtracts the height of the
|
86
|
+
# right subtree to produce the balance factor for the node.
|
87
|
+
def balance_factor(node)
|
88
|
+
node.nil? ? 0 : (height(node.left) - height(node.right))
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module BSTree
|
2
|
+
class BSTree
|
3
|
+
|
4
|
+
attr_reader :root
|
5
|
+
|
6
|
+
def initialize(root = nil)
|
7
|
+
@root = root
|
8
|
+
end
|
9
|
+
|
10
|
+
# Starts the search for the insertion at the root of
|
11
|
+
# the tree. Set the new root at the end if it has changed.
|
12
|
+
def insert_item(item)
|
13
|
+
@root = add_to_tree(item, @root)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Starts the search for the deletion at the root of
|
17
|
+
# the tree. Set the new root at the end if it has changed.
|
18
|
+
def remove_item(item)
|
19
|
+
@root = remove_from_tree(item, @root)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Starts the count at the root node.
|
23
|
+
def number_of_nodes
|
24
|
+
node_count(@root)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Starts the count of the depth at the root node.
|
28
|
+
def depth_of_tree
|
29
|
+
tree_depth(@root, 0)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# If the current node is null then a new Node object is created and item
|
35
|
+
# is passed in as a constructor. If item is smaller than the data stored
|
36
|
+
# in the current node then :add_to_tree is called recursively to traverse
|
37
|
+
# the left-sub tree until the node is null. The same thing happens if the
|
38
|
+
# item is larger, but the right sub-tree is traversed. If item is neither
|
39
|
+
# larger nor smaller then it must be equal, in which case an error will be
|
40
|
+
# raised to inform the user of duplication.
|
41
|
+
def add_to_tree(item, node)
|
42
|
+
if node.nil?
|
43
|
+
return Node::Node.new(item)
|
44
|
+
elsif compare(item, node.data) < 0
|
45
|
+
node.left = add_to_tree(item, node.left)
|
46
|
+
elsif compare(item, node.data) > 0
|
47
|
+
node.right = add_to_tree(item, node.right)
|
48
|
+
else
|
49
|
+
puts 'Duplicate entry. Skipping: %s' % item
|
50
|
+
end
|
51
|
+
return node
|
52
|
+
end
|
53
|
+
|
54
|
+
# If the current node is null then the tree is empty or the item isn't in it.
|
55
|
+
# If item is smaller or larger than current node like above, if it's neither then
|
56
|
+
# it must be equal so checks are made to see how many children that node has. If
|
57
|
+
# it has two then a the value of the node is swapped with the value of the
|
58
|
+
# smallest item in it's right-sub tree, then removeItem is called again but this time
|
59
|
+
# on the value in the right sub-tree. If the node has less than two children
|
60
|
+
# another check is made: if the left child is not null, the value of the node is
|
61
|
+
# equal to that value, otherwise it's equal to the value of the right child.
|
62
|
+
def remove_from_tree(item, node)
|
63
|
+
return if node.nil?
|
64
|
+
if compare(item, node.data) < 0
|
65
|
+
node.left = remove_from_tree(item, node.left)
|
66
|
+
elsif compare(item, node.data) > 0
|
67
|
+
node.right = remove_from_tree(item, node.right)
|
68
|
+
elsif node.left && node.right
|
69
|
+
node.data = least_item(node.right).data
|
70
|
+
node.right = remove_from_tree(node.data, node.right)
|
71
|
+
else
|
72
|
+
node = node.left ? node.left : node.right
|
73
|
+
end
|
74
|
+
return node
|
75
|
+
end
|
76
|
+
|
77
|
+
# Comparison of two objects. Will return -1 if item_one is less than item_two, 0 if
|
78
|
+
# they are equal, and 1 if item_one is greater than item_two. First checks if object
|
79
|
+
# has it's own comparison logic, if not, uses core Ruby comparision.
|
80
|
+
def compare(item_one, item_two)
|
81
|
+
begin
|
82
|
+
item_one.compare_to(item_two)
|
83
|
+
rescue NoMethodError
|
84
|
+
item_one <=> item_two
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Recursively finds the lowest item in a nodes left subtree.
|
89
|
+
def least_item(node)
|
90
|
+
return unless node
|
91
|
+
return node unless node.left
|
92
|
+
least_item(node.left)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Traverses the tree and returns the sum of the nodes.
|
96
|
+
def node_count(node)
|
97
|
+
node.nil? ? 0 : 1 + node_count(node.left) + node_count(node.right)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Traverses the tree and calculates the depth of the longest sub-tree
|
101
|
+
def tree_depth(node, depth)
|
102
|
+
node.nil? ? 0 : depth + max(height(node.left), height(node.right))
|
103
|
+
end
|
104
|
+
|
105
|
+
# Calculates the height of the node using it's leaves. Initial check to see if
|
106
|
+
# node is nil which means it has a height of 0.
|
107
|
+
def height(node)
|
108
|
+
node.nil? ? 0 : 1 + max(height(node.left), height(node.right))
|
109
|
+
end
|
110
|
+
|
111
|
+
# Converts parameters to an Array then calls Enumerable#max on them to return
|
112
|
+
# the largest element.
|
113
|
+
def max(*values)
|
114
|
+
values.max
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module BSTreeTraversal
|
2
|
+
class BSTreeTraversal
|
3
|
+
|
4
|
+
def pre_order_array(root_node, attribute = nil)
|
5
|
+
pre_order(root_node, attribute, [])
|
6
|
+
end
|
7
|
+
|
8
|
+
def pre_order_string(root_node, attribute = nil)
|
9
|
+
pre_order(root_node, attribute, []).join(', ')
|
10
|
+
end
|
11
|
+
|
12
|
+
def in_order_array(root_node, attribute = nil)
|
13
|
+
in_order(root_node, attribute, [])
|
14
|
+
end
|
15
|
+
|
16
|
+
def in_order_string(root_node, attribute = nil)
|
17
|
+
in_order(root_node, attribute, []).join(', ')
|
18
|
+
end
|
19
|
+
|
20
|
+
def post_order_array(root_node, attribute = nil)
|
21
|
+
post_order(root_node, attribute, [])
|
22
|
+
end
|
23
|
+
|
24
|
+
def post_order_string(root_node, attribute = nil)
|
25
|
+
post_order(root_node, attribute, []).join(', ')
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# The idea of preorder traversal is that we visit the nodes in the order root-left-right,
|
31
|
+
# meaning for any subtree in the path, root is processed as the node is visited, left node
|
32
|
+
# is visited second, followed by right node.
|
33
|
+
# If attribute is set then it is called as a method on the data stored
|
34
|
+
# in the node, and that value is added to the array. If not, the data is added.
|
35
|
+
def pre_order(node, attribute, node_list)
|
36
|
+
unless node.nil?
|
37
|
+
node_list << (attribute ? node.data.send(attribute) : node.data)
|
38
|
+
pre_order(node.left, attribute, node_list)
|
39
|
+
pre_order(node.right, attribute, node_list)
|
40
|
+
end
|
41
|
+
return node_list
|
42
|
+
end
|
43
|
+
|
44
|
+
# The idea of inorder traversal is that we visit the nodes in the order left-root-right,
|
45
|
+
# meaning for any subtree in the path, left node must be visited first followed by root
|
46
|
+
# and right node. Prints the values in ascending order.
|
47
|
+
# If attribute is set then it is called as a method on the data stored
|
48
|
+
# in the node, and that value is added to the array. If not, the data is added.
|
49
|
+
def in_order(node, attribute, node_list)
|
50
|
+
unless node.nil?
|
51
|
+
in_order(node.left, attribute, node_list)
|
52
|
+
node_list << (attribute ? node.data.send(attribute) : node.data)
|
53
|
+
in_order(node.right, attribute, node_list)
|
54
|
+
end
|
55
|
+
return node_list
|
56
|
+
end
|
57
|
+
|
58
|
+
# The idea of postorder traversal is that we visit the nodes in the order left-right-root,
|
59
|
+
# meaning for any subtree in the path, left node must be visited first, followed by the
|
60
|
+
# right node, and root is not processed until the children are.
|
61
|
+
# If attribute is set then it is called as a method on the data stored
|
62
|
+
# in the node, and that value is added to the array. If not, the data is added.
|
63
|
+
def post_order(node, attribute, node_list)
|
64
|
+
unless node.nil?
|
65
|
+
post_order(node.left, attribute, node_list)
|
66
|
+
post_order(node.right, attribute, node_list)
|
67
|
+
node_list << (attribute ? node.data.send(attribute) : node.data)
|
68
|
+
end
|
69
|
+
return node_list
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/ruby-avl.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.expand_path('../lib/ruby-avl/version', __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'ruby-avl'
|
5
|
+
s.version = AVL::VERSION
|
6
|
+
s.date = '2016-04-30'
|
7
|
+
s.description = s.summary = 'A simple AVL Tree implemented in Ruby'
|
8
|
+
s.authors = ['Daniel Byers']
|
9
|
+
s.email = 'daniel_byers@hotmail.co.uk'
|
10
|
+
s.homepage = 'http://rubygems.org/gems/ruby-avl'
|
11
|
+
s.license = 'MIT'
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.test_files = `git ls-files -- {test}/*`.split("\n")
|
15
|
+
s.require_path = 'lib'
|
16
|
+
|
17
|
+
s.add_development_dependency 'rake'
|
18
|
+
s.add_development_dependency 'rspec'
|
19
|
+
s.add_development_dependency 'rspec-given'
|
20
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
2
|
+
|
3
|
+
Given.use_natural_assertions
|
4
|
+
|
5
|
+
describe AVLTree::AVLTree do
|
6
|
+
Given(:node) { Node::Node.new(25) }
|
7
|
+
|
8
|
+
context 'initialize a AVL Tree with a null root' do
|
9
|
+
Then { subject.root == nil }
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'initialize a AVL Tree with a root node' do
|
13
|
+
Given(:subject) { AVLTree::AVLTree.new(node) }
|
14
|
+
Then { subject.root == node }
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'adding to AVL Tree with rebalancing' do
|
18
|
+
|
19
|
+
context 'left rotation' do
|
20
|
+
When(:result) {
|
21
|
+
subject.insert_item(25)
|
22
|
+
subject.insert_item(50)
|
23
|
+
subject.insert_item(100)
|
24
|
+
}
|
25
|
+
Then { result.data == 50 }
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'double left rotation' do
|
29
|
+
When(:result) {
|
30
|
+
subject.insert_item(25)
|
31
|
+
subject.insert_item(50)
|
32
|
+
subject.insert_item(100)
|
33
|
+
subject.insert_item(80)
|
34
|
+
subject.insert_item(70)
|
35
|
+
subject.insert_item(57)
|
36
|
+
}
|
37
|
+
Then { result.data == 70 }
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'right rotation' do
|
41
|
+
When(:result) {
|
42
|
+
subject.insert_item(25)
|
43
|
+
subject.insert_item(18)
|
44
|
+
subject.insert_item(7)
|
45
|
+
}
|
46
|
+
Then { result.data == 18 }
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'double right rotation' do
|
50
|
+
When(:result) {
|
51
|
+
subject.insert_item(25)
|
52
|
+
subject.insert_item(18)
|
53
|
+
subject.insert_item(7)
|
54
|
+
subject.insert_item(13)
|
55
|
+
subject.insert_item(12)
|
56
|
+
subject.insert_item(17)
|
57
|
+
}
|
58
|
+
Then { result.data == 13 }
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'no rotation' do
|
62
|
+
When(:result) {
|
63
|
+
subject.insert_item(25)
|
64
|
+
subject.insert_item(20)
|
65
|
+
subject.insert_item(30)
|
66
|
+
}
|
67
|
+
Then { result.data == 25 }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'removing from tree with rebalancing' do
|
72
|
+
|
73
|
+
When {
|
74
|
+
subject.insert_item(25)
|
75
|
+
subject.insert_item(50)
|
76
|
+
subject.insert_item(100)
|
77
|
+
subject.insert_item(80) # Visulisation:
|
78
|
+
subject.insert_item(70) # 70
|
79
|
+
subject.insert_item(57) # / \
|
80
|
+
subject.insert_item(72) # 50 80
|
81
|
+
subject.insert_item(77) # / \ / \
|
82
|
+
subject.insert_item(94) # 25 57 72 100
|
83
|
+
subject.insert_item(63) # \ \ \ /
|
84
|
+
subject.insert_item(30) # 30 63 77 94
|
85
|
+
}
|
86
|
+
|
87
|
+
context 'left rotation' do
|
88
|
+
When(:result) {
|
89
|
+
subject.remove_item(25)
|
90
|
+
subject.remove_item(30)
|
91
|
+
}
|
92
|
+
Then { result.left.data == 57 }
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'right rotation' do
|
96
|
+
When(:result) {
|
97
|
+
subject.insert_item(20)
|
98
|
+
subject.remove_item(30)
|
99
|
+
subject.remove_item(57)
|
100
|
+
subject.remove_item(63)
|
101
|
+
}
|
102
|
+
Then { result.left.data == 25 }
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'double left rotation rotation' do
|
106
|
+
When(:result) {
|
107
|
+
subject.remove_item(72)
|
108
|
+
subject.remove_item(77)
|
109
|
+
}
|
110
|
+
Then { result.right.data == 94 }
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'double right rotation' do
|
114
|
+
When(:result) {
|
115
|
+
subject.remove_item(100)
|
116
|
+
subject.remove_item(94)
|
117
|
+
}
|
118
|
+
Then { result.right.data == 77 }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
2
|
+
|
3
|
+
Given.use_natural_assertions
|
4
|
+
|
5
|
+
describe BSTree::BSTree do
|
6
|
+
Given(:node) { Node::Node.new(1) }
|
7
|
+
|
8
|
+
context 'initialize a BS Tree with a null root' do
|
9
|
+
Then { subject.root == nil }
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'initialize a BS Tree with a root node' do
|
13
|
+
Given(:subject) { BSTree::BSTree.new(node) }
|
14
|
+
Then { subject.root == node }
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'number of nodes in a tree' do
|
18
|
+
When {
|
19
|
+
subject.insert_item(1)
|
20
|
+
subject.insert_item(2)
|
21
|
+
subject.insert_item(3)
|
22
|
+
}
|
23
|
+
When(:result) { subject.number_of_nodes }
|
24
|
+
Then { result == 3 }
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'depth of the tree' do
|
28
|
+
When {
|
29
|
+
subject.insert_item(1)
|
30
|
+
subject.insert_item(2)
|
31
|
+
subject.insert_item(3)
|
32
|
+
}
|
33
|
+
When(:result) { subject.depth_of_tree }
|
34
|
+
Then { result == 2 }
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'adding items to the tree' do
|
38
|
+
|
39
|
+
context 'add a single item to the tree' do
|
40
|
+
When(:result) { subject.insert_item(1) }
|
41
|
+
Then { result.data == 1 }
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'add multiple items to the tree' do
|
45
|
+
When(:result) {
|
46
|
+
subject.insert_item(1)
|
47
|
+
subject.insert_item(2)
|
48
|
+
subject.insert_item(3)
|
49
|
+
subject.insert_item(4)
|
50
|
+
subject.insert_item(5)
|
51
|
+
}
|
52
|
+
Then { result.right.data == 2 }
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'adding duplicates should be ignored' do
|
56
|
+
When(:result) {
|
57
|
+
subject.insert_item(1)
|
58
|
+
subject.insert_item(1)
|
59
|
+
}
|
60
|
+
Then { result.data == 1}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'removing items from the tree' do
|
65
|
+
When {
|
66
|
+
subject.insert_item(3) # Visulisation:
|
67
|
+
subject.insert_item(2) # 3
|
68
|
+
subject.insert_item(1) # / \
|
69
|
+
subject.insert_item(5) # 2 5
|
70
|
+
subject.insert_item(4) # / / \
|
71
|
+
subject.insert_item(6) # 1 4 6
|
72
|
+
}
|
73
|
+
|
74
|
+
context 'removing a leaf node' do
|
75
|
+
When(:result) { subject.remove_item(1) }
|
76
|
+
Then { result.left.left == nil }
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'removing from a node with a left leaf' do
|
80
|
+
When(:result) { subject.remove_item(2) }
|
81
|
+
Then { result.left.data == 1 }
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'removing from a node with a right leaf' do
|
85
|
+
When(:result) { subject.remove_item(6) }
|
86
|
+
Then { result.right.right == nil }
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'removing from a node with two leaves' do
|
90
|
+
When(:result) { subject.remove_item(5) }
|
91
|
+
Then { result.right.right == nil }
|
92
|
+
And { result.right.left.data == 4 }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
2
|
+
|
3
|
+
Given.use_natural_assertions
|
4
|
+
|
5
|
+
describe BSTreeTraversal::BSTreeTraversal do
|
6
|
+
# Given returns the last node of the AVL Tree instead of the tree object itself.
|
7
|
+
# In production, avl_tree.root would need to be passed into the traversal methods.
|
8
|
+
Given(:avl_tree) {
|
9
|
+
avl_tree = AVLTree::AVLTree.new
|
10
|
+
avl_tree.insert_item(30) # Visulisation:
|
11
|
+
avl_tree.insert_item(10) # 23
|
12
|
+
avl_tree.insert_item(23) # / \
|
13
|
+
avl_tree.insert_item(55) # 10 47
|
14
|
+
avl_tree.insert_item(47) # / / \
|
15
|
+
avl_tree.insert_item(7) # 7 30 55
|
16
|
+
}
|
17
|
+
|
18
|
+
context 'AVL Tree pre order' do
|
19
|
+
context 'as array' do
|
20
|
+
When(:result) { subject.pre_order_array(avl_tree) }
|
21
|
+
Then { result == [23, 10, 7, 47, 30, 55] }
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'as string' do
|
25
|
+
When(:result) { subject.pre_order_string(avl_tree) }
|
26
|
+
Then { result == '23, 10, 7, 47, 30, 55' }
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'with certain attribute' do
|
30
|
+
When(:result) { subject.pre_order_string(avl_tree, :even) }
|
31
|
+
Then { result = 'False, True, False, False, True, False' }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'AVL Tree in order' do
|
36
|
+
context 'as array' do
|
37
|
+
When(:result) { subject.in_order_array(avl_tree) }
|
38
|
+
Then { result == [7, 10, 23, 30, 47, 55] }
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'as string' do
|
42
|
+
When(:result) { subject.in_order_string(avl_tree) }
|
43
|
+
Then { result == '7, 10, 23, 30, 47, 55' }
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'with certain attribute' do
|
47
|
+
When(:result) { subject.in_order_string(avl_tree, :even) }
|
48
|
+
Then { result = 'False, True, False, True, False, False' }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'AVL Tree post order' do
|
53
|
+
context 'as array' do
|
54
|
+
When(:result) { subject.post_order_array(avl_tree) }
|
55
|
+
Then { result == [7, 10, 30, 55, 47, 23] }
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'as string' do
|
59
|
+
When(:result) { subject.post_order_string(avl_tree) }
|
60
|
+
Then { result == '7, 10, 30, 55, 47, 23' }
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'with certain attribute' do
|
64
|
+
When(:result) { subject.post_order_string(avl_tree, :even) }
|
65
|
+
Then { result = 'False, True, True, False, False, False' }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/spec/node_spec.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
2
|
+
|
3
|
+
Given.use_natural_assertions
|
4
|
+
|
5
|
+
describe Node::Node do
|
6
|
+
context 'should initialize a new node' do
|
7
|
+
Given(:node) { Node::Node.new('test string') }
|
8
|
+
Then { node.data == 'test string' }
|
9
|
+
And { node.left == nil}
|
10
|
+
And { node.right == nil}
|
11
|
+
end
|
12
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rspec/given'
|
3
|
+
require 'ruby-avl'
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
# rspec-expectations config goes here. You can use an alternate
|
7
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
8
|
+
# assertions if you prefer.
|
9
|
+
config.expect_with :rspec do |expectations|
|
10
|
+
# This option will default to `true` in RSpec 4. It makes the `description`
|
11
|
+
# and `failure_message` of custom matchers include text for helper methods
|
12
|
+
# defined using `chain`, e.g.:
|
13
|
+
# be_bigger_than(2).and_smaller_than(4).description
|
14
|
+
# # => "be bigger than 2 and smaller than 4"
|
15
|
+
# ...rather than:
|
16
|
+
# # => "be bigger than 2"
|
17
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
18
|
+
end
|
19
|
+
|
20
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
21
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
22
|
+
config.mock_with :rspec do |mocks|
|
23
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
24
|
+
# a real object. This is generally recommended, and will default to
|
25
|
+
# `true` in RSpec 4.
|
26
|
+
mocks.verify_partial_doubles = true
|
27
|
+
end
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-avl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.0'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Daniel Byers
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-04-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec-given
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: A simple AVL Tree implemented in Ruby
|
56
|
+
email: daniel_byers@hotmail.co.uk
|
57
|
+
executables: []
|
58
|
+
extensions: []
|
59
|
+
extra_rdoc_files: []
|
60
|
+
files:
|
61
|
+
- ".gitignore"
|
62
|
+
- Gemfile
|
63
|
+
- README.md
|
64
|
+
- Rakefile
|
65
|
+
- lib/ruby-avl.rb
|
66
|
+
- lib/ruby-avl/avl_tree.rb
|
67
|
+
- lib/ruby-avl/bs_tree.rb
|
68
|
+
- lib/ruby-avl/bs_tree_traversal.rb
|
69
|
+
- lib/ruby-avl/node.rb
|
70
|
+
- lib/ruby-avl/version.rb
|
71
|
+
- ruby-avl.gemspec
|
72
|
+
- spec/avl_tree_spec.rb
|
73
|
+
- spec/bs_tree_spec.rb
|
74
|
+
- spec/bs_tree_traversal_spec.rb
|
75
|
+
- spec/node_spec.rb
|
76
|
+
- spec/spec_helper.rb
|
77
|
+
homepage: http://rubygems.org/gems/ruby-avl
|
78
|
+
licenses:
|
79
|
+
- MIT
|
80
|
+
metadata: {}
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 2.4.8
|
98
|
+
signing_key:
|
99
|
+
specification_version: 4
|
100
|
+
summary: A simple AVL Tree implemented in Ruby
|
101
|
+
test_files: []
|