DSA 0.0.1
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 +19 -0
- data/DSA.gemspec +24 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +23 -0
- data/README.md +127 -0
- data/Rakefile +2 -0
- data/lib/DSA.rb +10 -0
- data/lib/DSA/algorithm.rb +69 -0
- data/lib/DSA/binary_search_tree.rb +710 -0
- data/lib/DSA/list.rb +183 -0
- data/lib/DSA/priority_queue.rb +70 -0
- data/lib/DSA/stack_and_queue.rb +151 -0
- data/lib/DSA/version.rb +3 -0
- data/test/algorithms_test.rb +91 -0
- data/test/binary_search_tree_test.rb +287 -0
- data/test/list_test.rb +84 -0
- data/test/priority_queue_test.rb +49 -0
- data/test/stack_and_queue_test.rb +96 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f9927035d95a766e70b9f202d3ccba141d2665bd
|
4
|
+
data.tar.gz: 78444502a579279fe7d288e4fe48a5f24684a7f7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cf06eb363bfdc2b9e7aee1b746a73f106ac7919df4dc229008b32e9c06d06265ce1157a6191ad0c0af775b04a56c19e7007c78dd89d7b10b9450f89e9ae3191e
|
7
|
+
data.tar.gz: 34e8770d943e932144dcd1e9d61034f1fee333655ee128c32b033110ac9c3c5be3f890fd8da9fa778d7f5edb8aa04dbe08941cd856778963f6e4cc794706bc57
|
data/.gitignore
ADDED
data/DSA.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'DSA/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "DSA"
|
8
|
+
spec.version = DSA::VERSION
|
9
|
+
spec.authors = ["lusaisai"]
|
10
|
+
spec.email = ["lusaisai@163.com"]
|
11
|
+
spec.summary = %q{Data Structures and Algorithms in Ruby}
|
12
|
+
spec.description = %q{List, BinarySearchTree(RedBlackTree), PriorityQueue, (Array/List)Stack and Queue}
|
13
|
+
spec.homepage = "https://github.com/lusaisai/DSA"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
end
|
24
|
+
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (c) 2014 lusaisai
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
|
data/README.md
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
# DSA
|
2
|
+
|
3
|
+
Ruby gem for basic Data Structures and Algorithms
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'DSA'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install DSA
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
To include the package,
|
22
|
+
|
23
|
+
require 'DSA'
|
24
|
+
|
25
|
+
|
26
|
+
### List
|
27
|
+
A doubly linked list data structure. Use when there are lots of insertions/deletions in the middle,
|
28
|
+
otherwise, built-in array is better.
|
29
|
+
|
30
|
+
l = DSA::List.new
|
31
|
+
l.push 'some value'
|
32
|
+
l.pop
|
33
|
+
l.unshift 'some value'
|
34
|
+
l.shift
|
35
|
+
l.first
|
36
|
+
l.last
|
37
|
+
l.empty?
|
38
|
+
l.length
|
39
|
+
|
40
|
+
General access/removal/insertion using index is supported, these operations require linear time, so use carefully.
|
41
|
+
|
42
|
+
l[2]
|
43
|
+
l.insert_at 10, 'some value'
|
44
|
+
l.remove_at 2
|
45
|
+
|
46
|
+
To do lots of insertions/deletions, use the iterator, StopIteration is raised when reaching to head or tail
|
47
|
+
|
48
|
+
li = l.begin_iterator # the iterator starts from the head
|
49
|
+
puts li.next
|
50
|
+
li.insert 'some value'
|
51
|
+
li.remove
|
52
|
+
li.update 'new value'
|
53
|
+
|
54
|
+
li = l.end_iterator # the iterator starts from the tail
|
55
|
+
li.previous
|
56
|
+
|
57
|
+
Enumerable is included, all those method such as 'each' are all available, since other methods are based on each,
|
58
|
+
the performance might not be the best, use only when a full traversal is inevitable.
|
59
|
+
|
60
|
+
### BinarySearchTree
|
61
|
+
An ordered map, works like a hash, but preserves an order and provides range search, implemented as a RedBlack tree.
|
62
|
+
|
63
|
+
The following three are aliases in creating a new object,
|
64
|
+
|
65
|
+
rb = DSA::BinarySearchTree.new
|
66
|
+
rb = DSA::OrderedMap.new
|
67
|
+
rb = DSA::RedBlackTree.new
|
68
|
+
|
69
|
+
Method are very like a hash,
|
70
|
+
|
71
|
+
rb[key] = value
|
72
|
+
rb[key]
|
73
|
+
rb.delete key
|
74
|
+
|
75
|
+
And special methods related to orders, those methods yield key/value pairs to block, if no block, enumerator is returned.
|
76
|
+
|
77
|
+
rb.each # in-order traversal
|
78
|
+
rb.gt(key) # key/value pairs for keys greater than key
|
79
|
+
rb.ge(key)
|
80
|
+
rb.lt(key)
|
81
|
+
rb.le(key)
|
82
|
+
|
83
|
+
A help method tried to print a tree, not quite pretty, but may helps test
|
84
|
+
|
85
|
+
rb.bfs_print
|
86
|
+
|
87
|
+
Enumerable is included, all those method such as 'each' are all available, since other methods are based on each,
|
88
|
+
the performance might not be the best, use only when a full traversal is inevitable.
|
89
|
+
|
90
|
+
|
91
|
+
### PriorityQueue
|
92
|
+
An array based heap, priority is a number, the smaller it is, higher priority it has
|
93
|
+
|
94
|
+
pq = DSA::PriorityQueue.new
|
95
|
+
pq.add 10, 'some job'
|
96
|
+
pq.length
|
97
|
+
pq.top # look at the highest priority without removing it
|
98
|
+
job = pq.pop # get and remove the highest priority job
|
99
|
+
|
100
|
+
|
101
|
+
### Stack and Queue
|
102
|
+
Implemented based on array or list.
|
103
|
+
|
104
|
+
s = DSA::ArrayStack.new
|
105
|
+
s = DSA::ListStack.new
|
106
|
+
s.push 'some value'
|
107
|
+
s.pop
|
108
|
+
s.empty?
|
109
|
+
s.top
|
110
|
+
s.length
|
111
|
+
|
112
|
+
q = DSA::ArrayQueue.new
|
113
|
+
q = DSA::ListQueue.new
|
114
|
+
q.enqueue 'some value'
|
115
|
+
q.dequeue
|
116
|
+
q.empty?
|
117
|
+
q.first
|
118
|
+
q.length
|
119
|
+
|
120
|
+
### Algorithm
|
121
|
+
The following functions are for demonstrations, specially sort, using built-in Array#bsearch and Array#sort instead,
|
122
|
+
they have a better performance.
|
123
|
+
|
124
|
+
DSA::Algorithm::factorial(5)
|
125
|
+
DSA::Algorithm::binary_search((1..9).to_a, 2, 0, 8)
|
126
|
+
DSA::Algorithm::insertion_sort!(array)
|
127
|
+
DSA::Algorithm::quick_sort!(array, 0, array.length-1)
|
data/Rakefile
ADDED
data/lib/DSA.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require_relative 'DSA/version'
|
2
|
+
require_relative 'DSA/algorithm'
|
3
|
+
require_relative 'DSA/stack_and_queue'
|
4
|
+
require_relative 'DSA/list'
|
5
|
+
require_relative 'DSA/priority_queue'
|
6
|
+
require_relative 'DSA/binary_search_tree'
|
7
|
+
|
8
|
+
module DSA
|
9
|
+
# Your code goes here...
|
10
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module DSA
|
2
|
+
module Algorithm
|
3
|
+
|
4
|
+
# Factorial function, the "Hello World" program of recursion
|
5
|
+
def self.factorial(n)
|
6
|
+
if n == 0
|
7
|
+
1
|
8
|
+
else
|
9
|
+
n * factorial(n-1)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
# Binary search in sorted array
|
15
|
+
def self.binary_search(data, target, low, high)
|
16
|
+
return false if low > high
|
17
|
+
middle = (low + high) / 2
|
18
|
+
if data[middle] == target
|
19
|
+
true
|
20
|
+
elsif target < data[middle]
|
21
|
+
binary_search data, target, low, middle-1
|
22
|
+
else
|
23
|
+
binary_search data, target, middle+1, high
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Insertion sort, n square worst running time, should not be used in general, built-in array sort is very good
|
28
|
+
def self.insertion_sort!(data)
|
29
|
+
data.length.times do |index|
|
30
|
+
this_value = data[index]
|
31
|
+
j = index - 1
|
32
|
+
while j >= -1
|
33
|
+
if data[j] > this_value && j != -1
|
34
|
+
data[j+1] = data[j]
|
35
|
+
else
|
36
|
+
data[j+1] = this_value
|
37
|
+
break
|
38
|
+
end
|
39
|
+
j -= 1
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# in place quick sort
|
45
|
+
def self.quick_sort!(data, low, high)
|
46
|
+
return if low >= high
|
47
|
+
pivot = data[high]
|
48
|
+
left = low
|
49
|
+
right = high - 1
|
50
|
+
while left <= right
|
51
|
+
until left > right || data[left] >= pivot
|
52
|
+
left += 1
|
53
|
+
end
|
54
|
+
until left > right || data[right] <= pivot
|
55
|
+
right -= 1
|
56
|
+
end
|
57
|
+
if left <= right
|
58
|
+
data[left], data[right] = data[right], data[left]
|
59
|
+
left += 1
|
60
|
+
right -= 1
|
61
|
+
end
|
62
|
+
end
|
63
|
+
data[left], data[high] = data[high], data[left]
|
64
|
+
quick_sort!(data, low, left - 1)
|
65
|
+
quick_sort!(data, left+1, high)
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,710 @@
|
|
1
|
+
module DSA
|
2
|
+
# A basic binary search tree node
|
3
|
+
class BasicBinarySearchTreeNode
|
4
|
+
attr_accessor :key, :value, :parent, :left, :right
|
5
|
+
def initialize(key, value)
|
6
|
+
@key = key
|
7
|
+
@value = value
|
8
|
+
@parent = nil
|
9
|
+
@left = nil
|
10
|
+
@right = nil
|
11
|
+
end
|
12
|
+
end
|
13
|
+
# A basic binary search tree(or ordered map), with no specific self balancing
|
14
|
+
class BasicBinarySearchTree
|
15
|
+
include Enumerable
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@root = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def []=(key, value)
|
22
|
+
new_node = BasicBinarySearchTreeNode.new(key, value)
|
23
|
+
if @root.nil?
|
24
|
+
@root = new_node
|
25
|
+
else
|
26
|
+
insert(@root, new_node)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def delete(key)
|
31
|
+
return nil if @root.nil?
|
32
|
+
node = find_node @root, key
|
33
|
+
return nil if node.nil?
|
34
|
+
delete_node(node).first.value
|
35
|
+
end
|
36
|
+
|
37
|
+
def [](key)
|
38
|
+
return nil if @root.nil?
|
39
|
+
node = find_node @root, key
|
40
|
+
if node
|
41
|
+
node.value
|
42
|
+
else
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# yield the key/value pair for all those great than input key
|
48
|
+
def gt(key)
|
49
|
+
return nil if @root.nil?
|
50
|
+
|
51
|
+
if block_given?
|
52
|
+
node = find_gt_node @root, key
|
53
|
+
if node
|
54
|
+
yield [node.key, node.value]
|
55
|
+
loop do
|
56
|
+
node = in_order_next_node node
|
57
|
+
if node
|
58
|
+
yield [node.key, node.value]
|
59
|
+
else
|
60
|
+
break
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
else
|
65
|
+
Enumerator.new do |y|
|
66
|
+
gt(key) do |key, value|
|
67
|
+
y << [key, value]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
# yield the key/value pair for all those less than input key
|
76
|
+
def lt(key)
|
77
|
+
return nil if @root.nil?
|
78
|
+
|
79
|
+
if block_given?
|
80
|
+
node = find_lt_node @root, key
|
81
|
+
if node
|
82
|
+
yield [node.key, node.value]
|
83
|
+
loop do
|
84
|
+
node = in_order_prev_node node
|
85
|
+
if node
|
86
|
+
yield [node.key, node.value]
|
87
|
+
else
|
88
|
+
break
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
else
|
93
|
+
Enumerator.new do |y|
|
94
|
+
lt(key) do |key, value|
|
95
|
+
y << [key, value]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# yield the key/value pair for all those great than or equal to input key
|
102
|
+
def ge(key)
|
103
|
+
return nil if @root.nil?
|
104
|
+
|
105
|
+
if block_given?
|
106
|
+
node = find_node @root, key
|
107
|
+
yield [node.key, node.value] if node
|
108
|
+
node = find_gt_node @root, key
|
109
|
+
if node
|
110
|
+
yield [node.key, node.value]
|
111
|
+
loop do
|
112
|
+
node = in_order_next_node node
|
113
|
+
if node
|
114
|
+
yield [node.key, node.value]
|
115
|
+
else
|
116
|
+
break
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
else
|
121
|
+
Enumerator.new do |y|
|
122
|
+
ge(key) do |key, value|
|
123
|
+
y << [key, value]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# yield the key/value pair for all those great than or equal to input key
|
130
|
+
def le(key)
|
131
|
+
return nil if @root.nil?
|
132
|
+
|
133
|
+
if block_given?
|
134
|
+
node = find_node @root, key
|
135
|
+
yield [node.key, node.value] if node
|
136
|
+
node = find_lt_node @root, key
|
137
|
+
if node
|
138
|
+
yield [node.key, node.value]
|
139
|
+
loop do
|
140
|
+
node = in_order_prev_node node
|
141
|
+
if node
|
142
|
+
yield [node.key, node.value]
|
143
|
+
else
|
144
|
+
break
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
else
|
149
|
+
Enumerator.new do |y|
|
150
|
+
le(key) do |key, value|
|
151
|
+
y << [key, value]
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
def each
|
160
|
+
return if @root.nil?
|
161
|
+
if block_given?
|
162
|
+
in_order_traversal @root, Proc.new
|
163
|
+
else
|
164
|
+
Enumerator.new do |y|
|
165
|
+
each do |key, value|
|
166
|
+
y << [key, value]
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def min
|
173
|
+
if @root.nil?
|
174
|
+
nil
|
175
|
+
else
|
176
|
+
min_node(@root).value
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def max
|
181
|
+
if @root.nil?
|
182
|
+
nil
|
183
|
+
else
|
184
|
+
max_node(@root).value
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# breadth first traversal
|
189
|
+
def bfs_print
|
190
|
+
puts '=' * 100
|
191
|
+
level = [@root]
|
192
|
+
until level.empty?
|
193
|
+
next_level = []
|
194
|
+
level.each do |node|
|
195
|
+
if node
|
196
|
+
printf "<#{node.key}, #{node.value}>"
|
197
|
+
printf "(parent_is_#{node.parent.key})\t" if node != @root
|
198
|
+
next_level.push node.left
|
199
|
+
next_level.push node.right
|
200
|
+
else
|
201
|
+
printf "<Nil>\t"
|
202
|
+
next_level.push nil
|
203
|
+
next_level.push nil
|
204
|
+
end
|
205
|
+
end
|
206
|
+
puts
|
207
|
+
if next_level.count(nil) < next_level.length
|
208
|
+
level = next_level
|
209
|
+
else
|
210
|
+
level = []
|
211
|
+
end
|
212
|
+
end
|
213
|
+
puts '=' * 100
|
214
|
+
end
|
215
|
+
|
216
|
+
protected
|
217
|
+
def in_order_next_node(node)
|
218
|
+
# if it has a right subtree, take the left most of right subtree
|
219
|
+
if !node.right.nil?
|
220
|
+
return min_node node.right
|
221
|
+
|
222
|
+
# if it is a left child, take the parent
|
223
|
+
# if it is a right child, go up till we find a left child's parent, otherwise finished
|
224
|
+
else
|
225
|
+
while !node.nil?
|
226
|
+
return node.parent if left_child? node
|
227
|
+
node = node.parent
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
nil
|
232
|
+
end
|
233
|
+
|
234
|
+
def in_order_prev_node(node)
|
235
|
+
# if it has a left subtree, take the right most of left subtree
|
236
|
+
if !node.left.nil?
|
237
|
+
return max_node node.left
|
238
|
+
|
239
|
+
# if it is a right child, take the parent
|
240
|
+
# if it is a left child, go up till we find a right child's parent, otherwise finished
|
241
|
+
else
|
242
|
+
while !node.nil?
|
243
|
+
return node.parent if right_child? node
|
244
|
+
node = node.parent
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
nil
|
249
|
+
end
|
250
|
+
|
251
|
+
def left_child?(node)
|
252
|
+
parent = node.parent
|
253
|
+
if parent
|
254
|
+
parent.left == node
|
255
|
+
else
|
256
|
+
false
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def right_child?(node)
|
261
|
+
parent = node.parent
|
262
|
+
if parent
|
263
|
+
parent.right == node
|
264
|
+
else
|
265
|
+
false
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
|
270
|
+
# in-order traversal
|
271
|
+
def in_order_traversal(node, block)
|
272
|
+
unless node.left.nil?
|
273
|
+
in_order_traversal(node.left, block)
|
274
|
+
end
|
275
|
+
block.call(node.key, node.value)
|
276
|
+
unless node.right.nil?
|
277
|
+
in_order_traversal(node.right, block)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
def delete_node(node)
|
282
|
+
if node.left.nil? && node.right.nil? # it's a leaf
|
283
|
+
replace node, nil
|
284
|
+
elsif ! node.left.nil? && node.right.nil? # only has a left child
|
285
|
+
replace node, node.left
|
286
|
+
elsif node.left.nil? && ! node.right.nil? # only has a right child
|
287
|
+
replace node, node.right
|
288
|
+
else # has both children
|
289
|
+
next_node = min_node node.right
|
290
|
+
node.key = next_node.key
|
291
|
+
node.value = next_node.value
|
292
|
+
delete_node next_node
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
# replace a node with another node, this includes the links that node has
|
297
|
+
def replace(node, new_node)
|
298
|
+
if node == @root
|
299
|
+
@root = new_node
|
300
|
+
sibling = nil
|
301
|
+
else
|
302
|
+
parent = node.parent
|
303
|
+
if parent.left == node
|
304
|
+
parent.left = new_node
|
305
|
+
sibling = parent.right
|
306
|
+
else
|
307
|
+
parent.right = new_node
|
308
|
+
sibling = parent.left
|
309
|
+
end
|
310
|
+
new_node.parent = parent unless new_node.nil?
|
311
|
+
end
|
312
|
+
[node, new_node, sibling]
|
313
|
+
end
|
314
|
+
|
315
|
+
|
316
|
+
# find the minimum node of a subtree
|
317
|
+
def min_node(node)
|
318
|
+
if node.left.nil?
|
319
|
+
node
|
320
|
+
else
|
321
|
+
min_node node.left
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
# find the maximum node of a subtree
|
326
|
+
def max_node(node)
|
327
|
+
if node.right.nil?
|
328
|
+
node
|
329
|
+
else
|
330
|
+
max_node node.right
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
# find a node through a key in a subtree
|
335
|
+
def find_node(node, key)
|
336
|
+
if node.key == key
|
337
|
+
node
|
338
|
+
elsif key < node.key
|
339
|
+
if node.left
|
340
|
+
find_node node.left, key
|
341
|
+
else
|
342
|
+
nil
|
343
|
+
end
|
344
|
+
else
|
345
|
+
if node.right
|
346
|
+
find_node node.right, key
|
347
|
+
else
|
348
|
+
nil
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
end
|
353
|
+
|
354
|
+
# find first node that is greater than the key in a subtree
|
355
|
+
def find_gt_node(node, key)
|
356
|
+
if key == node.key
|
357
|
+
in_order_next_node node
|
358
|
+
elsif key < node.key
|
359
|
+
if node.left
|
360
|
+
find_gt_node node.left, key
|
361
|
+
else
|
362
|
+
node
|
363
|
+
end
|
364
|
+
else
|
365
|
+
if node.right
|
366
|
+
find_gt_node node.right, key
|
367
|
+
else
|
368
|
+
in_order_next_node node
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
end
|
373
|
+
|
374
|
+
# find first node that is less than the key in a subtree
|
375
|
+
def find_lt_node(node, key)
|
376
|
+
if key == node.key
|
377
|
+
in_order_prev_node node
|
378
|
+
elsif key < node.key
|
379
|
+
if node.left
|
380
|
+
find_lt_node node.left, key
|
381
|
+
else
|
382
|
+
in_order_prev_node node
|
383
|
+
end
|
384
|
+
else
|
385
|
+
if node.right
|
386
|
+
find_lt_node node.right, key
|
387
|
+
else
|
388
|
+
node
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
end
|
393
|
+
|
394
|
+
# insert a new node into the binary search (sub)tree
|
395
|
+
def insert(node, new_node)
|
396
|
+
if node.key == new_node.key
|
397
|
+
node.value = new_node.value
|
398
|
+
return node
|
399
|
+
elsif new_node.key < node.key
|
400
|
+
if node.left.nil?
|
401
|
+
node.left = new_node
|
402
|
+
new_node.parent = node
|
403
|
+
return new_node
|
404
|
+
else
|
405
|
+
insert( node.left, new_node )
|
406
|
+
end
|
407
|
+
else
|
408
|
+
if node.right.nil?
|
409
|
+
node.right = new_node
|
410
|
+
new_node.parent = node
|
411
|
+
return new_node
|
412
|
+
else
|
413
|
+
insert( node.right, new_node )
|
414
|
+
end
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
end
|
419
|
+
|
420
|
+
class RedBlackTreeNode < BasicBinarySearchTreeNode
|
421
|
+
RED = 0
|
422
|
+
BLACK = 1
|
423
|
+
def initialize(key, value)
|
424
|
+
super(key, value)
|
425
|
+
@color = RED
|
426
|
+
end
|
427
|
+
|
428
|
+
def red?
|
429
|
+
@color == RED
|
430
|
+
end
|
431
|
+
|
432
|
+
def black?
|
433
|
+
@color == BLACK
|
434
|
+
end
|
435
|
+
|
436
|
+
def set_black!
|
437
|
+
@color = BLACK
|
438
|
+
end
|
439
|
+
|
440
|
+
def set_red!
|
441
|
+
@color = RED
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
class RedBlackTree < BasicBinarySearchTree
|
446
|
+
|
447
|
+
def []=(key, value)
|
448
|
+
new_node = RedBlackTreeNode.new(key, value)
|
449
|
+
if @root.nil?
|
450
|
+
new_node.set_black!
|
451
|
+
@root = new_node
|
452
|
+
else
|
453
|
+
node = insert(@root, new_node)
|
454
|
+
fix_variance node if node.red? # when it is an existing node, it could be black
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
def delete(key)
|
459
|
+
return nil if @root.nil?
|
460
|
+
node = find_node @root, key
|
461
|
+
return nil if node.nil?
|
462
|
+
rb_delete_node(node).first.value
|
463
|
+
end
|
464
|
+
|
465
|
+
# breadth first traversal
|
466
|
+
def bfs_print
|
467
|
+
puts '=' * 100
|
468
|
+
level = [@root]
|
469
|
+
until level.empty?
|
470
|
+
next_level = []
|
471
|
+
level.each do |node|
|
472
|
+
if node
|
473
|
+
printf "<#{node.key}, #{node.value}>"
|
474
|
+
printf "(#{node.parent.key}|" if node != @root
|
475
|
+
if node.red?
|
476
|
+
printf "R)\t"
|
477
|
+
else
|
478
|
+
printf "B)\t"
|
479
|
+
end
|
480
|
+
next_level.push node.left
|
481
|
+
next_level.push node.right
|
482
|
+
else
|
483
|
+
printf "<Nil>\t"
|
484
|
+
next_level.push nil
|
485
|
+
next_level.push nil
|
486
|
+
end
|
487
|
+
end
|
488
|
+
puts
|
489
|
+
if next_level.count(nil) < next_level.length
|
490
|
+
level = next_level
|
491
|
+
else
|
492
|
+
level = []
|
493
|
+
end
|
494
|
+
end
|
495
|
+
puts '=' * 100
|
496
|
+
end
|
497
|
+
|
498
|
+
private
|
499
|
+
def rb_delete_node(node)
|
500
|
+
node, replace_node, sibling = delete_node(node)
|
501
|
+
value = [node, replace_node]
|
502
|
+
|
503
|
+
return value if node.red? # red is always fine
|
504
|
+
if node.black?
|
505
|
+
if replace_node # if it has a child(the replace_node), it must be red
|
506
|
+
replace_node.set_black!
|
507
|
+
else # it is a black leaf, it is complicated
|
508
|
+
fix_deficit sibling
|
509
|
+
end
|
510
|
+
end
|
511
|
+
|
512
|
+
value
|
513
|
+
end
|
514
|
+
|
515
|
+
# the heavier side after a black leaf deleted
|
516
|
+
def fix_deficit(sibling)
|
517
|
+
parent = sibling.parent
|
518
|
+
if sibling.black?
|
519
|
+
x = has_red_child? sibling
|
520
|
+
if x
|
521
|
+
deletion_restructure(x)
|
522
|
+
else # if sibling does not have a red child
|
523
|
+
sibling.set_red!
|
524
|
+
if parent.red?
|
525
|
+
parent.set_black!
|
526
|
+
else
|
527
|
+
fix_deficit sibling_of(parent)
|
528
|
+
end
|
529
|
+
end
|
530
|
+
else # when my sibling is red
|
531
|
+
if left_child? sibling
|
532
|
+
heavy_node = sibling.right
|
533
|
+
right_up_rotation sibling
|
534
|
+
else
|
535
|
+
heavy_node = sibling.left
|
536
|
+
left_up_rotation sibling
|
537
|
+
end
|
538
|
+
sibling.set_black!
|
539
|
+
parent.set_red!
|
540
|
+
fix_deficit heavy_node
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
def has_red_child?(node)
|
545
|
+
if node.left && node.left.red?
|
546
|
+
node.left
|
547
|
+
elsif node.right && node.right.red?
|
548
|
+
node.right
|
549
|
+
else
|
550
|
+
nil
|
551
|
+
end
|
552
|
+
end
|
553
|
+
|
554
|
+
def fix_variance(node)
|
555
|
+
# for root, recolor to black when necessary
|
556
|
+
if node == @root
|
557
|
+
node.set_black! if node.red?
|
558
|
+
return
|
559
|
+
end
|
560
|
+
|
561
|
+
parent = node.parent
|
562
|
+
return if parent.black? # we are good
|
563
|
+
|
564
|
+
# otherwise we have the double red
|
565
|
+
grand_parent = parent.parent
|
566
|
+
uncle = sibling_of parent
|
567
|
+
|
568
|
+
if uncle && uncle.red?
|
569
|
+
parent.set_black!
|
570
|
+
uncle.set_black!
|
571
|
+
grand_parent.set_red!
|
572
|
+
fix_variance grand_parent
|
573
|
+
else
|
574
|
+
restructure( node )
|
575
|
+
end
|
576
|
+
|
577
|
+
end
|
578
|
+
|
579
|
+
# rotations and re-color in different cases(zig-zig and zig-zag) for deletion
|
580
|
+
def deletion_restructure(node)
|
581
|
+
parent = node.parent
|
582
|
+
grand_parent = parent.parent
|
583
|
+
if left_child?(node) && left_child?(parent)
|
584
|
+
right_up_rotation parent
|
585
|
+
if grand_parent.red?
|
586
|
+
parent.set_red!
|
587
|
+
else
|
588
|
+
parent.set_black!
|
589
|
+
end
|
590
|
+
node.set_black!
|
591
|
+
grand_parent.set_black!
|
592
|
+
elsif right_child?(node) && left_child?(parent)
|
593
|
+
left_up_rotation node
|
594
|
+
right_up_rotation node
|
595
|
+
if grand_parent.red?
|
596
|
+
node.set_red!
|
597
|
+
else
|
598
|
+
node.set_black!
|
599
|
+
end
|
600
|
+
parent.set_black!
|
601
|
+
grand_parent.set_black!
|
602
|
+
elsif right_child?(node) && right_child?(parent)
|
603
|
+
left_up_rotation parent
|
604
|
+
if grand_parent.red?
|
605
|
+
parent.set_red!
|
606
|
+
else
|
607
|
+
parent.set_black!
|
608
|
+
end
|
609
|
+
node.set_black!
|
610
|
+
grand_parent.set_black!
|
611
|
+
else
|
612
|
+
right_up_rotation node
|
613
|
+
left_up_rotation node
|
614
|
+
if grand_parent.red?
|
615
|
+
node.set_red!
|
616
|
+
else
|
617
|
+
node.set_black!
|
618
|
+
end
|
619
|
+
parent.set_black!
|
620
|
+
grand_parent.set_black!
|
621
|
+
end
|
622
|
+
end
|
623
|
+
|
624
|
+
# rotations and re-color in different cases(zig-zig and zig-zag) for insertion
|
625
|
+
def restructure(node)
|
626
|
+
parent = node.parent
|
627
|
+
grand_parent = parent.parent
|
628
|
+
if left_child?(node) && left_child?(parent)
|
629
|
+
right_up_rotation parent
|
630
|
+
parent.set_black!
|
631
|
+
node.set_red!
|
632
|
+
grand_parent.set_red!
|
633
|
+
elsif right_child?(node) && left_child?(parent)
|
634
|
+
left_up_rotation node
|
635
|
+
right_up_rotation node
|
636
|
+
node.set_black!
|
637
|
+
parent.set_red!
|
638
|
+
grand_parent.set_red!
|
639
|
+
elsif right_child?(node) && right_child?(parent)
|
640
|
+
left_up_rotation parent
|
641
|
+
parent.set_black!
|
642
|
+
node.set_red!
|
643
|
+
grand_parent.set_red!
|
644
|
+
else
|
645
|
+
right_up_rotation node
|
646
|
+
left_up_rotation node
|
647
|
+
node.set_black!
|
648
|
+
parent.set_red!
|
649
|
+
grand_parent.set_red!
|
650
|
+
end
|
651
|
+
end
|
652
|
+
|
653
|
+
def sibling_of(node)
|
654
|
+
parent = node.parent
|
655
|
+
return nil if parent.nil?
|
656
|
+
if left_child? node
|
657
|
+
parent.right
|
658
|
+
else
|
659
|
+
parent.left
|
660
|
+
end
|
661
|
+
end
|
662
|
+
|
663
|
+
def right_up_rotation(node)
|
664
|
+
parent = node.parent
|
665
|
+
grand_parent = parent.parent
|
666
|
+
|
667
|
+
node.parent = grand_parent
|
668
|
+
if left_child? parent
|
669
|
+
grand_parent.left = node
|
670
|
+
elsif right_child? parent
|
671
|
+
grand_parent.right = node
|
672
|
+
else
|
673
|
+
@root = node
|
674
|
+
end
|
675
|
+
|
676
|
+
parent.left = node.right
|
677
|
+
node.right.parent = parent if node.right
|
678
|
+
|
679
|
+
node.right = parent
|
680
|
+
parent.parent = node
|
681
|
+
|
682
|
+
end
|
683
|
+
|
684
|
+
def left_up_rotation(node)
|
685
|
+
parent = node.parent
|
686
|
+
grand_parent = parent.parent
|
687
|
+
|
688
|
+
node.parent = grand_parent
|
689
|
+
if left_child? parent
|
690
|
+
grand_parent.left = node
|
691
|
+
elsif right_child? parent
|
692
|
+
grand_parent.right = node
|
693
|
+
else
|
694
|
+
@root = node
|
695
|
+
end
|
696
|
+
|
697
|
+
parent.right = node.left
|
698
|
+
node.left.parent = parent if node.left
|
699
|
+
|
700
|
+
node.left = parent
|
701
|
+
parent.parent = node
|
702
|
+
|
703
|
+
end
|
704
|
+
|
705
|
+
|
706
|
+
end
|
707
|
+
|
708
|
+
BinarySearchTree = RedBlackTree
|
709
|
+
OrderedMap = RedBlackTree
|
710
|
+
end
|