ruby-avl 0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|