search_tree 1.1.0.2
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/README.md +189 -0
- data/lib/bi-dimensional-access.rb +375 -0
- data/lib/bi-dimensional-node.rb +12 -0
- data/lib/binary-tree-access.rb +153 -0
- data/lib/binary-tree-node.rb +11 -0
- data/lib/search_tree.rb +4 -0
- metadata +47 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 919b31f780576cc1906242bb950b6a1ee9e4cfd9c704e46bbf5a1f80509d6ddc
|
4
|
+
data.tar.gz: e585b587411334cc9f27579def854b5c38ad1fc9688af36f88f69eb70b834316
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a085087340e456d665f5dd2b4e98549cf34cd91dd8b37f42774fa5c5aa20e8b5407e4eb4a1f949a47d8ce3936f411594d6dfd78381b4ea5e10abb784a2673d30
|
7
|
+
data.tar.gz: 9d27ee80fcfddfb7168fb0cd413926f287c8cf9a4aedc6ce6ed21dc41f3ad9a70d62a35569764709ddfe951ef48977528b8f0037098f780c032b81629ea68539
|
data/README.md
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
# Search-tree - Open source search binary tree and bi-dimensional tree.
|
2
|
+
|
3
|
+
[![Forks][forks-shield]][forks-url] [![Stars][stars-shield]][stars-url] [![Issues][issues-shield]][issues-url] [![Inline docs][docs-shield]][docs-url] [![Twitter][twitter-shield]][twitter-url]
|
4
|
+
|
5
|
+
Search tree is an open-source binary and bi-dimensional tree gem for ruby.
|
6
|
+
For more information about why and how it was done visit my [article][article-url] in [Medium][medium-phalado].
|
7
|
+
|
8
|
+
<p align="center">
|
9
|
+
<a href="https://github.com/phalado/Search-tree/issues">Report Bug</a>
|
10
|
+
- <a href="https://github.com/phalado/Search-tree/issues">Request Feature</a>
|
11
|
+
</p>
|
12
|
+
|
13
|
+
<!-- TABLE OF CONTENTS -->
|
14
|
+
## Table of Contents
|
15
|
+
|
16
|
+
* [Install and Usage](#install-and-usage)
|
17
|
+
* [Available Methods](#avaiable-methods)
|
18
|
+
* [New node](#new-node)
|
19
|
+
* [Search](#search)
|
20
|
+
* [Search node](#search_node)
|
21
|
+
* [Edit node](#edit_node)
|
22
|
+
* [Delete node](#delete-node)
|
23
|
+
* [Print tree](#print-tree)
|
24
|
+
* [Number of nodes](#number-of-nodes)
|
25
|
+
* [Get depth](#get-depth)
|
26
|
+
* [Is balanced](#is-balanced)
|
27
|
+
* [Balance](#balance)
|
28
|
+
* [Get nodes](#get-nodes)
|
29
|
+
* [New balance nodes](#new-balance-nodes)
|
30
|
+
* [New node balanced](#new-node-balanced)
|
31
|
+
* [Load file](#load-file)
|
32
|
+
* [Save file](#save-file)
|
33
|
+
* [Author and Contribution](#author-and-contribution)
|
34
|
+
* [License](#license)
|
35
|
+
* [Future Works](#future-works)
|
36
|
+
|
37
|
+
## Install and Usage
|
38
|
+
|
39
|
+
Download the [gem file][gem-file] and use the following command to install:
|
40
|
+
```bash
|
41
|
+
gem install search_tree-[version].gem
|
42
|
+
```
|
43
|
+
After installing add
|
44
|
+
```ruby
|
45
|
+
require 'search_tree'
|
46
|
+
```
|
47
|
+
in your code and you can use its methods.
|
48
|
+
|
49
|
+
## Available Methods
|
50
|
+
|
51
|
+
First, you have to create a new tree using the ``BinaryTree`` class, as for example below:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
t = BinaryTree.new
|
55
|
+
```
|
56
|
+
In the ``initialize`` method the **root** variable is defined as *nil*.
|
57
|
+
|
58
|
+
### New node
|
59
|
+
```ruby
|
60
|
+
new_node(node, x, *args)
|
61
|
+
```
|
62
|
+
This method is used to create a new node in the tree. It receives the **node** where the search begins, **x** as the search parameter and the pointer **args** that will receive all the other arguments added to the node.
|
63
|
+
|
64
|
+
### Search
|
65
|
+
```ruby
|
66
|
+
search(x, node = @root)
|
67
|
+
```
|
68
|
+
Returns *true* if, starting at the passed **node**, it finds a node with the search parameter **x** and *false* if it finds a *nil*.
|
69
|
+
|
70
|
+
### Search node
|
71
|
+
```ruby
|
72
|
+
search_node(x, node = @root)
|
73
|
+
```
|
74
|
+
Like the previus one but instead of returning *true* or *false* returns the pointer to the node with search parameter **x** or *nil*.
|
75
|
+
|
76
|
+
### Edit node
|
77
|
+
```ruby
|
78
|
+
edit_node(x, *args)
|
79
|
+
```
|
80
|
+
Locate the node with search parameter **x** and changer its *arguments* for the ones in **args**, if this node existis.
|
81
|
+
|
82
|
+
### Delete node
|
83
|
+
```ruby
|
84
|
+
delete_node(x)
|
85
|
+
```
|
86
|
+
Delete the node with the dearch parameter **x**, if exists.
|
87
|
+
|
88
|
+
### Print tree
|
89
|
+
```ruby
|
90
|
+
print_tree(node = @root)
|
91
|
+
```
|
92
|
+
Print the tree, or sub-tree starting at the passed **node**, in crescent search parameter order.
|
93
|
+
|
94
|
+
### Number of nodes
|
95
|
+
```ruby
|
96
|
+
number_nodes(node = @root)
|
97
|
+
```
|
98
|
+
Return the number of nodes in the tree, or sub-tree starting at the passed **node**.
|
99
|
+
|
100
|
+
### Get depth
|
101
|
+
```ruby
|
102
|
+
get_depth(node = @root, depth = 1, maxdepth = 0)
|
103
|
+
```
|
104
|
+
Return the depth of the tree, or sub-tree starting at the passed **node**. The **depth** and **maxdepth** are used as helpers inside the method.
|
105
|
+
|
106
|
+
### Is balanced
|
107
|
+
```ruby
|
108
|
+
is_balanced?
|
109
|
+
```
|
110
|
+
Return *true* if the number of nodes is smaller than ``2 ^ (depth - 1)`` and bigger than ``2 ^ depth`` and *false* otherwise.
|
111
|
+
|
112
|
+
### Balance
|
113
|
+
```ruby
|
114
|
+
balance
|
115
|
+
```
|
116
|
+
Balance the tree unless ``is_balanced?`` returns *true*.
|
117
|
+
|
118
|
+
### Get nodes
|
119
|
+
```ruby
|
120
|
+
get_nodes(nodes, node = @root)
|
121
|
+
```
|
122
|
+
Return the array **nodes** with all the nodes in the crescent search parameter order in the tree or sub-tree starting at the passed **node**. Used to balance the tree.
|
123
|
+
|
124
|
+
### New balance nodes
|
125
|
+
```ruby
|
126
|
+
new_balance_nodes(nodes, newroot = @root)
|
127
|
+
```
|
128
|
+
Used to create a new node with the array **nodes** received in the ``get_nodes`` method.
|
129
|
+
|
130
|
+
### New node balanced
|
131
|
+
```ruby
|
132
|
+
new_node_balanced(node, x, *args)
|
133
|
+
```
|
134
|
+
Create a new node and balance the tree.
|
135
|
+
|
136
|
+
### Load file
|
137
|
+
```ruby
|
138
|
+
load_file(file)
|
139
|
+
```
|
140
|
+
Create a tree with the data inside a file. The search parameter will always be a string.
|
141
|
+
|
142
|
+
### Save file
|
143
|
+
```ruby
|
144
|
+
save_file(file)
|
145
|
+
```
|
146
|
+
Create a file with the data in the tree.
|
147
|
+
|
148
|
+
## Author and Contribution
|
149
|
+
|
150
|
+
Add me at [linkedin][linkedin-url], send me an [email][phalado@gmail.com], visit my [twitter][twitter-url], [medium][medium-phalado] and [portfolio][my-portfolio].
|
151
|
+
|
152
|
+
Feel free to contribute with pull requests but, for major changes, please open an issue first.
|
153
|
+
|
154
|
+
## License
|
155
|
+
|
156
|
+
Coming soon
|
157
|
+
|
158
|
+
## Future works
|
159
|
+
|
160
|
+
Already started a load file and a save file methods.
|
161
|
+
|
162
|
+
Have a big ambition to create the bi-dimensional tree. I will do it in the next days.
|
163
|
+
|
164
|
+
<!-- MARKDOWN LINKS & IMAGES -->
|
165
|
+
<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
|
166
|
+
[downloads-shield]: https://img.shields.io/github/downloads/phalado/Search-tree
|
167
|
+
[downloads-url]: https://github.com/ferreirati/mv-08-htmlcss-framework/graphs/contributors
|
168
|
+
|
169
|
+
[forks-shield]: https://img.shields.io/github/forks/phalado/Search-tree
|
170
|
+
[forks-url]: https://github.com/phalado/Search-tree/network/members
|
171
|
+
|
172
|
+
[stars-shield]: https://img.shields.io/github/stars/phalado/Search-tree
|
173
|
+
[stars-url]: https://github.com/phalado/Search-tree/stargazers
|
174
|
+
|
175
|
+
[issues-shield]: https://img.shields.io/github/issues/phalado/Search-tree
|
176
|
+
[issues-url]: https://github.com/phalado/Search-tree/issues
|
177
|
+
|
178
|
+
[docs-shield]: http://inch-ci.org/github/phalado/Search-tree.svg?branch=master
|
179
|
+
[docs-url]: http://inch-ci.org/github/phalado/Search-tree
|
180
|
+
|
181
|
+
[twitter-shield]: https://img.shields.io/twitter/url?url=https%3A%2F%2Fgithub.com%2Fphalado%2FSearch-tree%2F
|
182
|
+
[twitter-url]: https://twitter.com/Phalado
|
183
|
+
[article-url]: https://medium.com/p/bdfe7069be2d/
|
184
|
+
[medium-phalado]: https://medium.com/@phalado
|
185
|
+
[gem-file]: http://.com/
|
186
|
+
[linkedin-url]: https://www.linkedin.com/in/raphael-cordeiro/
|
187
|
+
[my-portfolio]: https://phalado.github.io/
|
188
|
+
|
189
|
+
[product-screenshot]: images/screenshot.png
|
@@ -0,0 +1,375 @@
|
|
1
|
+
class BiDimensionalTree
|
2
|
+
attr_reader :root
|
3
|
+
|
4
|
+
# Initialize the tree creating it's root
|
5
|
+
def initialize
|
6
|
+
@root = nil
|
7
|
+
end
|
8
|
+
|
9
|
+
# Create a new branch after locating it's place
|
10
|
+
def new_node(node, x, y, *args)
|
11
|
+
if @root.nil?
|
12
|
+
puts "Adding the node (#{x}, #{y}) in the root"
|
13
|
+
return @root = BiNode.new(x, y, *args)
|
14
|
+
elsif node.nil?
|
15
|
+
return node = BiNode.new(x, y, *args)
|
16
|
+
elsif node.x == x
|
17
|
+
if node.y == y
|
18
|
+
return node
|
19
|
+
elsif y < node.y
|
20
|
+
if node.south.nil?
|
21
|
+
puts "Adding the node (#{x}, #{y}) in the south of (#{node.x}, #{node.y})"
|
22
|
+
node.south = new_node(node.south, x, y, *args)
|
23
|
+
else
|
24
|
+
new_node(node.south, x, y, *args)
|
25
|
+
end
|
26
|
+
elsif y > node.y
|
27
|
+
if node.north.nil?
|
28
|
+
puts "Adding the node (#{x}, #{y}) in the north of (#{node.x}, #{node.y})"
|
29
|
+
node.north = new_node(node.north, x, y, *args)
|
30
|
+
else
|
31
|
+
new_node(node.north, x, y, *args)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
elsif x < node.x
|
35
|
+
if node.west.nil?
|
36
|
+
puts "Adding the node (#{x}, #{y}) in the west of (#{node.x}, #{node.y})"
|
37
|
+
node.west = new_node(node.west, x, y, *args)
|
38
|
+
else
|
39
|
+
new_node(node.west, x, y, *args)
|
40
|
+
end
|
41
|
+
elsif x > node.x
|
42
|
+
if node.east.nil?
|
43
|
+
puts "Adding the node (#{x}, #{y}) in the east of (#{node.x}, #{node.y})"
|
44
|
+
node.east = new_node(node.east, x, y, *args)
|
45
|
+
else
|
46
|
+
new_node(node.east, x, y, *args)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Create a new node and balance the tree
|
52
|
+
def new_node_balanced(node, x, y, *args)
|
53
|
+
new_node(node, x, y, *args)
|
54
|
+
balance
|
55
|
+
end
|
56
|
+
|
57
|
+
# Locate a branch, if it exists
|
58
|
+
def search(x, y, node = @root)
|
59
|
+
return false if node.nil?
|
60
|
+
|
61
|
+
if x == node.x
|
62
|
+
if y == node.y
|
63
|
+
return true
|
64
|
+
elsif y < node.y and !node.south.nil?
|
65
|
+
search(x, y, node.south)
|
66
|
+
elsif y > node.y and !node.north.nil?
|
67
|
+
search(x, y, node.north)
|
68
|
+
else
|
69
|
+
return false
|
70
|
+
end
|
71
|
+
elsif x < node.x and !node.west.nil?
|
72
|
+
search(x, y, node.west)
|
73
|
+
elsif x > node.x and !node.east.nil?
|
74
|
+
search(x, y, node.east)
|
75
|
+
else
|
76
|
+
return false
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Locate a branch, if it exists
|
81
|
+
def search_node(x, y, node = @root)
|
82
|
+
return nil if node.nil?
|
83
|
+
|
84
|
+
if x == node.x
|
85
|
+
if y == node.y
|
86
|
+
return node
|
87
|
+
elsif y < node.y and !node.south.nil?
|
88
|
+
search_node(x, y, node.south)
|
89
|
+
elsif y > node.y and !node.north.nil?
|
90
|
+
search_node(x, y, node.north)
|
91
|
+
else
|
92
|
+
return nil
|
93
|
+
end
|
94
|
+
elsif x < node.x and !node.west.nil?
|
95
|
+
search_node(x, y, node.west)
|
96
|
+
elsif x > node.x and !node.east.nil?
|
97
|
+
search_node(x, y, node.east)
|
98
|
+
else
|
99
|
+
return nil
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Edit an existing node
|
104
|
+
def edit_node(x, y, *args)
|
105
|
+
node = search_node(x, y)
|
106
|
+
return if node.nil?
|
107
|
+
node.args = args
|
108
|
+
end
|
109
|
+
|
110
|
+
# Load a file and create a (sub)tree with its data
|
111
|
+
def load_file(file)
|
112
|
+
data = File.open(file).readlines.map(&:chomp)
|
113
|
+
data.each do |d|
|
114
|
+
d = d.split(',')
|
115
|
+
new_node(@root, *d[0], *d[1], *d[2..-1]) unless d.nil?
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Save the current tree in a file
|
120
|
+
def save_file
|
121
|
+
nodes = get_nodes([])
|
122
|
+
file = File.open('search_tree', 'w')
|
123
|
+
nodes.each do |node|
|
124
|
+
file.puts("#{node.x} #{node.y} #{node.args}")
|
125
|
+
end
|
126
|
+
file.close
|
127
|
+
end
|
128
|
+
|
129
|
+
# Print all the branchs
|
130
|
+
def print_tree(node = @root)
|
131
|
+
return if node.nil?
|
132
|
+
print_tree(node.west) unless node.west.nil?
|
133
|
+
print_tree(node.south) unless node.south.nil?
|
134
|
+
puts "#{node.x} #{node.y} #{node.args}"
|
135
|
+
print_tree(node.north) unless node.north.nil?
|
136
|
+
print_tree(node.east) unless node.east.nil?
|
137
|
+
return
|
138
|
+
end
|
139
|
+
|
140
|
+
# Delete a selected node from the tree
|
141
|
+
def delete_node(x, y)
|
142
|
+
node = search_node(x, y)
|
143
|
+
nodes = get_nodes_horiz([])
|
144
|
+
if nodes.include? node
|
145
|
+
nodes.delete(node)
|
146
|
+
@root = nodes[nodes.length / 2]
|
147
|
+
@root.west = nil
|
148
|
+
@root.east = nil
|
149
|
+
|
150
|
+
@root.west = new_balance_nodes_horiz(nodes[0..(nodes.length / 2) - 1])
|
151
|
+
@root.east = new_balance_nodes_horiz(nodes[(nodes.length / 2) + 1..-1])
|
152
|
+
else
|
153
|
+
nodes = get_column(node)
|
154
|
+
nodes.delete(node)
|
155
|
+
middle = nodes[nodes.length / 2]
|
156
|
+
middle.south = nil
|
157
|
+
middle.north = nil
|
158
|
+
|
159
|
+
middle.south = new_balance_nodes_vert(nodes[0..(nodes.length / 2) - 1])
|
160
|
+
middle.north = new_balance_nodes_vert(nodes[(nodes.length / 2) + 1..-1])
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Balance the tree
|
165
|
+
def balance_vert(node = @root)
|
166
|
+
unless balanced_vert_helper(node)
|
167
|
+
balance_vert_helper(node, node)
|
168
|
+
end
|
169
|
+
unless node.east.nil?
|
170
|
+
unless balanced_vert_helper(node.east)
|
171
|
+
node.east = balance_vert_helper(node.east, node.east)
|
172
|
+
else
|
173
|
+
balance_vert(node.east)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
unless node.west.nil?
|
177
|
+
unless balanced_vert_helper(node.west)
|
178
|
+
node.west = balance_vert_helper(node.west, node.west)
|
179
|
+
else
|
180
|
+
balance_vert(node.west)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def balance_horiz(node = @root)
|
186
|
+
return if balanced_horiz?
|
187
|
+
|
188
|
+
nodes = get_nodes_horiz([], node)
|
189
|
+
|
190
|
+
@root = nodes[nodes.length / 2]
|
191
|
+
@root.west = nil
|
192
|
+
@root.east = nil
|
193
|
+
|
194
|
+
@root.west = new_balance_nodes_horiz(nodes[0..(nodes.length / 2) - 1])
|
195
|
+
@root.east = new_balance_nodes_horiz(nodes[(nodes.length / 2) + 1..-1])
|
196
|
+
end
|
197
|
+
|
198
|
+
# Return true if tree is balanced, in other word,
|
199
|
+
# if the number of branchs is smaller than 2 raised
|
200
|
+
# to the tree's depth
|
201
|
+
def balanced_horiz?
|
202
|
+
number_nodes_horiz.between?(2**(get_depth_horiz - 1), 2**get_depth_horiz)
|
203
|
+
end
|
204
|
+
|
205
|
+
def balanced_vert?(node = @root)
|
206
|
+
return false unless balanced_vert_helper(node)
|
207
|
+
unless node.east.nil?
|
208
|
+
return false unless balanced_vert?(node.east)
|
209
|
+
end
|
210
|
+
unless node.west.nil?
|
211
|
+
return false unless balanced_vert?(node.west)
|
212
|
+
end
|
213
|
+
true
|
214
|
+
end
|
215
|
+
|
216
|
+
def balance
|
217
|
+
balance_horiz unless balanced_horiz?
|
218
|
+
balance_vert unless balanced_vert?
|
219
|
+
end
|
220
|
+
|
221
|
+
def balanced_tree
|
222
|
+
return false unless balanced_horiz?
|
223
|
+
return false unless balanced_vert?
|
224
|
+
true
|
225
|
+
end
|
226
|
+
|
227
|
+
# Return the max depth of the tree
|
228
|
+
def get_depth_horiz(node = @root, depth = 1, maxdepth = 0)
|
229
|
+
unless node.west.nil?
|
230
|
+
maxdepth = get_depth_horiz(node.west, depth + 1, maxdepth)
|
231
|
+
end
|
232
|
+
|
233
|
+
unless node.east.nil?
|
234
|
+
maxdepth = get_depth_horiz(node.east, depth + 1, maxdepth)
|
235
|
+
end
|
236
|
+
|
237
|
+
if node.west.nil? and node.east.nil? and depth > maxdepth
|
238
|
+
maxdepth = depth
|
239
|
+
end
|
240
|
+
|
241
|
+
maxdepth
|
242
|
+
end
|
243
|
+
|
244
|
+
def get_depth_vert(node = @root, depth = 1, maxdepth = 0)
|
245
|
+
unless node.south.nil?
|
246
|
+
maxdepth = get_depth_vert(node.south, depth + 1, maxdepth)
|
247
|
+
end
|
248
|
+
|
249
|
+
unless node.north.nil?
|
250
|
+
maxdepth = get_depth_vert(node.north, depth + 1, maxdepth)
|
251
|
+
end
|
252
|
+
|
253
|
+
if node.south.nil? and node.north.nil? and depth > maxdepth
|
254
|
+
maxdepth = depth
|
255
|
+
end
|
256
|
+
|
257
|
+
maxdepth
|
258
|
+
end
|
259
|
+
|
260
|
+
# Return the number of branchs
|
261
|
+
def number_nodes_horiz(node = @root)
|
262
|
+
if node.nil?
|
263
|
+
0
|
264
|
+
else
|
265
|
+
1 +
|
266
|
+
number_nodes_horiz(node.west) +
|
267
|
+
number_nodes_horiz(node.east)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
def number_nodes_vert(node = @root)
|
272
|
+
if node.nil?
|
273
|
+
0
|
274
|
+
else
|
275
|
+
1 +
|
276
|
+
number_nodes_vert(node.south) +
|
277
|
+
number_nodes_vert(node.north)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
def number_nodes_total(node = @root)
|
282
|
+
if node.nil?
|
283
|
+
0
|
284
|
+
else
|
285
|
+
1 +
|
286
|
+
number_nodes_total(node.west) +
|
287
|
+
number_nodes_total(node.east) +
|
288
|
+
number_nodes_total(node.south) +
|
289
|
+
number_nodes_total(node.north)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
private
|
294
|
+
|
295
|
+
# Return all the nodes in crescent order
|
296
|
+
def get_nodes_horiz(nodes, node = @root)
|
297
|
+
nodes = get_nodes_horiz(nodes, node.west) unless node.west.nil?
|
298
|
+
nodes << node
|
299
|
+
nodes = get_nodes_horiz(nodes, node.east) unless node.east.nil?
|
300
|
+
return nodes
|
301
|
+
end
|
302
|
+
|
303
|
+
def new_balance_nodes_horiz(nodes)
|
304
|
+
return nil if nodes.empty?
|
305
|
+
|
306
|
+
new_middle = nodes[nodes.length / 2]
|
307
|
+
new_middle.east = nil
|
308
|
+
new_middle.west = nil
|
309
|
+
|
310
|
+
return new_middle if nodes.length == 1
|
311
|
+
|
312
|
+
new_middle.west = new_balance_nodes_horiz(nodes[0..(nodes.length / 2) - 1])
|
313
|
+
new_middle.east = new_balance_nodes_horiz(nodes[(nodes.length / 2) + 1..-1])
|
314
|
+
|
315
|
+
return new_middle
|
316
|
+
end
|
317
|
+
|
318
|
+
def get_nodes_vert(nodes, node = @root)
|
319
|
+
nodes = get_nodes_vert(nodes, node.south) unless node.south.nil?
|
320
|
+
nodes << node
|
321
|
+
nodes = get_nodes_vert(nodes, node.north) unless node.north.nil?
|
322
|
+
return nodes
|
323
|
+
end
|
324
|
+
|
325
|
+
def balance_vert_helper(node, partial_root)
|
326
|
+
nodes = get_nodes_vert([], node)
|
327
|
+
|
328
|
+
middle = nodes[nodes.length / 2]
|
329
|
+
if partial_root.east
|
330
|
+
middle.east = partial_root.east
|
331
|
+
partial_root.east = nil
|
332
|
+
end
|
333
|
+
if partial_root.west
|
334
|
+
middle.west = partial_root.west
|
335
|
+
partial_root.west = nil
|
336
|
+
end
|
337
|
+
middle.north = nil
|
338
|
+
middle.south = nil
|
339
|
+
|
340
|
+
middle.south = new_balance_nodes_vert(nodes[0..(nodes.length / 2) - 1])
|
341
|
+
middle.north = new_balance_nodes_vert(nodes[(nodes.length / 2) + 1..-1])
|
342
|
+
|
343
|
+
return middle
|
344
|
+
end
|
345
|
+
|
346
|
+
def new_balance_nodes_vert(nodes)
|
347
|
+
return nil if nodes.empty?
|
348
|
+
|
349
|
+
new_middle = nodes[nodes.length / 2]
|
350
|
+
new_middle.north = nil
|
351
|
+
new_middle.south = nil
|
352
|
+
|
353
|
+
return new_middle if nodes.length == 1
|
354
|
+
|
355
|
+
new_middle.south = new_balance_nodes_vert(nodes[0..(nodes.length / 2) - 1])
|
356
|
+
new_middle.north = new_balance_nodes_vert(nodes[(nodes.length / 2) + 1..-1])
|
357
|
+
|
358
|
+
return new_middle
|
359
|
+
end
|
360
|
+
|
361
|
+
def balanced_vert_helper(node)
|
362
|
+
number_nodes_vert(node).between?(2**(get_depth_vert(node) - 1), 2**get_depth_vert(node))
|
363
|
+
end
|
364
|
+
|
365
|
+
def get_column(node, middle = @root)
|
366
|
+
return [] if middle.nil?
|
367
|
+
nodes = get_nodes_vert([], middle)
|
368
|
+
return nodes if nodes.include? node
|
369
|
+
nodes = get_column(node, middle.west) unless middle.west.nil?
|
370
|
+
return nodes if nodes.include? node
|
371
|
+
nodes = get_column(node, middle.east) unless middle.east.nil?
|
372
|
+
return nodes if nodes.include? node
|
373
|
+
return []
|
374
|
+
end
|
375
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class BiNode
|
2
|
+
attr_reader :x, :y
|
3
|
+
attr_accessor :east, :west, :north, :south, :args
|
4
|
+
|
5
|
+
# Create the tree's root with the base values x
|
6
|
+
# and y and an unkown number of arguments.
|
7
|
+
def initialize(x, y, *args)
|
8
|
+
@x = x
|
9
|
+
@y = y
|
10
|
+
@args = args
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
class BinaryTree
|
2
|
+
attr_reader :root
|
3
|
+
|
4
|
+
# Initialize the tree creating it's root
|
5
|
+
def initialize
|
6
|
+
@root = nil
|
7
|
+
end
|
8
|
+
|
9
|
+
# Create a new branch after locating it's place
|
10
|
+
def new_node(node, x, *args)
|
11
|
+
return @root = Node.new(x, *args) if @root.nil?
|
12
|
+
return Node.new(x, *args) if node.nil?
|
13
|
+
return node if node.x == x
|
14
|
+
|
15
|
+
if x < node.x
|
16
|
+
if node.west.nil?
|
17
|
+
node.west = new_node(node.west, x, *args)
|
18
|
+
else
|
19
|
+
new_node(node.west, x, *args)
|
20
|
+
end
|
21
|
+
elsif node.east.nil?
|
22
|
+
node.east = new_node(node.east, x, *args)
|
23
|
+
else
|
24
|
+
new_node(node.east, x, *args)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Create a new node and balance the tree
|
29
|
+
def new_node_balanced(node, x, *args)
|
30
|
+
new_node(node, x, *args)
|
31
|
+
balance unless balanced?
|
32
|
+
end
|
33
|
+
|
34
|
+
# Edit an existing node
|
35
|
+
def edit_node(x, *args)
|
36
|
+
node = search_node(x)
|
37
|
+
return if node.nil?
|
38
|
+
node.args = args
|
39
|
+
end
|
40
|
+
|
41
|
+
# Load a file and create a (sub)tree with its data
|
42
|
+
def load_file(file)
|
43
|
+
data = File.open(file).readlines.map(&:chomp)
|
44
|
+
data.each do |d|
|
45
|
+
d = d.split(',')
|
46
|
+
new_node(@root, *d[0], *d[1..-1]) unless d.nil?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Save the current tree in a file
|
51
|
+
def save_file
|
52
|
+
nodes = get_nodes([])
|
53
|
+
file = File.open('search_tree', 'w')
|
54
|
+
nodes.each do |node|
|
55
|
+
file.puts("#{node.x} #{node.args}")
|
56
|
+
end
|
57
|
+
file.close
|
58
|
+
end
|
59
|
+
|
60
|
+
# Delete a selected node from the tree
|
61
|
+
def delete_node(x)
|
62
|
+
nodes = get_nodes([])
|
63
|
+
node = search_node(x)
|
64
|
+
nodes.delete(node) if nodes.include? node
|
65
|
+
@root = nil
|
66
|
+
new_balance_nodes(nodes, @root)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Locate a branch, if it exists
|
70
|
+
def search(x, node = @root)
|
71
|
+
return false if node.nil?
|
72
|
+
|
73
|
+
if x == node.x
|
74
|
+
return true
|
75
|
+
elsif x < node.x && !node.west.nil?
|
76
|
+
search(x, node.west)
|
77
|
+
elsif x > node.x && !node.east.nil?
|
78
|
+
search(x, node.east)
|
79
|
+
else
|
80
|
+
return false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Locate a branch, if it exists
|
85
|
+
def search_node(x, node = @root)
|
86
|
+
return nil if node.nil?
|
87
|
+
|
88
|
+
if x == node.x
|
89
|
+
return node
|
90
|
+
elsif x < node.x && !node.west.nil?
|
91
|
+
search_node(x, node.west)
|
92
|
+
elsif x > node.x && !node.east.nil?
|
93
|
+
search_node(x, node.east)
|
94
|
+
else
|
95
|
+
return nil
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Print all the branchs
|
100
|
+
def print_tree(node = @root)
|
101
|
+
return if node.nil?
|
102
|
+
print_tree(node.west) unless node.west.nil?
|
103
|
+
puts "#{node.x} #{node.args}"
|
104
|
+
print_tree(node.east) unless node.east.nil?
|
105
|
+
end
|
106
|
+
|
107
|
+
# Return all the nodes in crescent order
|
108
|
+
def get_nodes(nodes, node = @root)
|
109
|
+
nodes = get_nodes(nodes, node.west) unless node.west.nil?
|
110
|
+
nodes << node
|
111
|
+
nodes = get_nodes(nodes, node.east) unless node.east.nil?
|
112
|
+
nodes
|
113
|
+
end
|
114
|
+
|
115
|
+
# Balance the tree
|
116
|
+
def balance
|
117
|
+
unless balanced?
|
118
|
+
nodes = get_nodes([])
|
119
|
+
@root = nil
|
120
|
+
new_balance_nodes(nodes, @root)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def new_balance_nodes(nodes, newroot = @root)
|
125
|
+
# p nodes
|
126
|
+
return if nodes.empty?
|
127
|
+
new_node(newroot, nodes[nodes.length / 2].x, *nodes[nodes.length / 2].args)
|
128
|
+
return if nodes.length == 1
|
129
|
+
new_balance_nodes(nodes[0..(nodes.length / 2) - 1], @root)
|
130
|
+
new_balance_nodes(nodes[(nodes.length / 2) + 1..-1], @root)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Return true if tree is balanced, in other word,
|
134
|
+
# if the number of branchs is smaller than 2 raised
|
135
|
+
# to the tree's depth
|
136
|
+
def balanced?
|
137
|
+
number_nodes.between?(2**(get_depth - 1), 2**get_depth)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Return the max depth of the tree
|
141
|
+
def get_depth(node = @root, depth = 1, maxdepth = 0)
|
142
|
+
maxdepth = get_depth(node.west, depth + 1, maxdepth) unless node.west.nil?
|
143
|
+
maxdepth = get_depth(node.east, depth + 1, maxdepth) unless node.east.nil?
|
144
|
+
maxdepth = depth if node.west.nil? && node.east.nil? && depth > maxdepth
|
145
|
+
|
146
|
+
maxdepth
|
147
|
+
end
|
148
|
+
|
149
|
+
# Return the number of branchs
|
150
|
+
def number_nodes(node = @root)
|
151
|
+
node.nil? ? 0 : 1 + number_nodes(node.west) + number_nodes(node.east)
|
152
|
+
end
|
153
|
+
end
|
data/lib/search_tree.rb
ADDED
metadata
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: search_tree
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- "@Phalado"
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-01-01 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email: phalado@gmail.com
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- README.md
|
20
|
+
- lib/bi-dimensional-access.rb
|
21
|
+
- lib/bi-dimensional-node.rb
|
22
|
+
- lib/binary-tree-access.rb
|
23
|
+
- lib/binary-tree-node.rb
|
24
|
+
- lib/search_tree.rb
|
25
|
+
homepage: https://github.com/phalado/Search-tree/
|
26
|
+
licenses: []
|
27
|
+
metadata: {}
|
28
|
+
post_install_message:
|
29
|
+
rdoc_options: []
|
30
|
+
require_paths:
|
31
|
+
- lib
|
32
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0'
|
37
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
requirements: []
|
43
|
+
rubygems_version: 3.0.6
|
44
|
+
signing_key:
|
45
|
+
specification_version: 4
|
46
|
+
summary: Open-source binary and bi-dimensional tree for ruby.
|
47
|
+
test_files: []
|