Skip to content

Commit 84f9954

Browse files
committed
Red-black tree benchmark from Charlie's GoGaRuCo talk.
https://github.com/headius/gogaruco2012/blob/master/bench_red_black.rb
1 parent 6016535 commit 84f9954

1 file changed

Lines changed: 361 additions & 0 deletions

File tree

bench/bench_red_black.rb

Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
require 'benchmark'
2+
3+
# Algorithm based on "Introduction to Algorithms" by Cormen and others
4+
class RedBlackTree
5+
class Node
6+
attr_accessor :color
7+
attr_accessor :key
8+
attr_accessor :left
9+
attr_accessor :right
10+
attr_accessor :parent
11+
12+
RED = :red
13+
BLACK = :black
14+
COLORS = [RED, BLACK].freeze
15+
16+
def initialize(key, color = RED)
17+
raise ArgumentError, "Bad value for color parameter" unless COLORS.include?(color)
18+
@color = color
19+
@key = key
20+
@left = @right = @parent = NilNode.instance
21+
end
22+
23+
def black?
24+
return color == BLACK
25+
end
26+
27+
def red?
28+
return color == RED
29+
end
30+
end
31+
32+
class NilNode < Node
33+
class << self
34+
private :new
35+
36+
# it's not thread safe
37+
def instance
38+
@instance ||= begin
39+
def instance
40+
return @instance
41+
end
42+
43+
new
44+
end
45+
end
46+
end
47+
48+
def initialize
49+
self.color = BLACK
50+
self.key = 0
51+
self.left = nil
52+
self.right = nil
53+
self.parent = nil
54+
end
55+
56+
def nil?
57+
return true
58+
end
59+
end
60+
61+
include Enumerable
62+
63+
attr_accessor :root
64+
attr_accessor :size
65+
66+
def initialize
67+
self.root = NilNode.instance
68+
self.size = 0
69+
end
70+
71+
def add(key)
72+
insert(Node.new(key))
73+
end
74+
75+
def insert(x)
76+
insert_helper(x)
77+
78+
x.color = Node::RED
79+
while x != root && x.parent.color == Node::RED
80+
if x.parent == x.parent.parent.left
81+
y = x.parent.parent.right
82+
if !y.nil? && y.color == Node::RED
83+
x.parent.color = Node::BLACK
84+
y.color = Node::BLACK
85+
x.parent.parent.color = Node::RED
86+
x = x.parent.parent
87+
else
88+
if x == x.parent.right
89+
x = x.parent
90+
left_rotate(x)
91+
end
92+
x.parent.color = Node::BLACK
93+
x.parent.parent.color = Node::RED
94+
right_rotate(x.parent.parent)
95+
end
96+
else
97+
y = x.parent.parent.left
98+
if !y.nil? && y.color == Node::RED
99+
x.parent.color = Node::BLACK
100+
y.color = Node::BLACK
101+
x.parent.parent.color = Node::RED
102+
x = x.parent.parent
103+
else
104+
if x == x.parent.left
105+
x = x.parent
106+
right_rotate(x)
107+
end
108+
x.parent.color = Node::BLACK
109+
x.parent.parent.color = Node::RED
110+
left_rotate(x.parent.parent)
111+
end
112+
end
113+
end
114+
root.color = Node::BLACK
115+
end
116+
117+
alias << insert
118+
119+
def delete(z)
120+
y = (z.left.nil? || z.right.nil?) ? z : successor(z)
121+
x = y.left.nil? ? y.right : y.left
122+
x.parent = y.parent
123+
124+
if y.parent.nil?
125+
self.root = x
126+
else
127+
if y == y.parent.left
128+
y.parent.left = x
129+
else
130+
y.parent.right = x
131+
end
132+
end
133+
134+
z.key = y.key if y != z
135+
136+
if y.color == Node::BLACK
137+
delete_fixup(x)
138+
end
139+
140+
self.size -= 1
141+
return y
142+
end
143+
144+
def minimum(x = root)
145+
while !x.left.nil?
146+
x = x.left
147+
end
148+
return x
149+
end
150+
151+
def maximum(x = root)
152+
while !x.right.nil?
153+
x = x.right
154+
end
155+
return x
156+
end
157+
158+
def successor(x)
159+
if !x.right.nil?
160+
return minimum(x.right)
161+
end
162+
y = x.parent
163+
while !y.nil? && x == y.right
164+
x = y
165+
y = y.parent
166+
end
167+
return y
168+
end
169+
170+
def predecessor(x)
171+
if !x.left.nil?
172+
return maximum(x.left)
173+
end
174+
y = x.parent
175+
while !y.nil? && x == y.left
176+
x = y
177+
y = y.parent
178+
end
179+
return y
180+
end
181+
182+
def inorder_walk(x = root)
183+
x = self.minimum
184+
while !x.nil?
185+
yield x.key
186+
x = successor(x)
187+
end
188+
end
189+
190+
alias each inorder_walk
191+
192+
def reverse_inorder_walk(x = root)
193+
x = self.maximum
194+
while !x.nil?
195+
yield x.key
196+
x = predecessor(x)
197+
end
198+
end
199+
200+
alias reverse_each reverse_inorder_walk
201+
202+
def search(key, x = root)
203+
while !x.nil? && x.key != key
204+
key < x.key ? x = x.left : x = x.right
205+
end
206+
return x
207+
end
208+
209+
def empty?
210+
return self.root.nil?
211+
end
212+
213+
def black_height(x = root)
214+
height = 0
215+
while !x.nil?
216+
x = x.left
217+
height +=1 if x.nil? || x.black?
218+
end
219+
return height
220+
end
221+
222+
private
223+
224+
def left_rotate(x)
225+
raise "x.right is nil!" if x.right.nil?
226+
y = x.right
227+
x.right = y.left
228+
y.left.parent = x if !y.left.nil?
229+
y.parent = x.parent
230+
if x.parent.nil?
231+
self.root = y
232+
else
233+
if x == x.parent.left
234+
x.parent.left = y
235+
else
236+
x.parent.right = y
237+
end
238+
end
239+
y.left = x
240+
x.parent = y
241+
end
242+
243+
def right_rotate(x)
244+
raise "x.left is nil!" if x.left.nil?
245+
y = x.left
246+
x.left = y.right
247+
y.right.parent = x if !y.right.nil?
248+
y.parent = x.parent
249+
if x.parent.nil?
250+
self.root = y
251+
else
252+
if x == x.parent.left
253+
x.parent.left = y
254+
else
255+
x.parent.right = y
256+
end
257+
end
258+
y.right = x
259+
x.parent = y
260+
end
261+
262+
def insert_helper(z)
263+
y = NilNode.instance
264+
x = root
265+
while !x.nil?
266+
y = x
267+
z.key < x.key ? x = x.left : x = x.right
268+
end
269+
z.parent = y
270+
if y.nil?
271+
self.root = z
272+
else
273+
z.key < y.key ? y.left = z : y.right = z
274+
end
275+
self.size += 1
276+
end
277+
278+
def delete_fixup(x)
279+
while x != root && x.color == Node::BLACK
280+
if x == x.parent.left
281+
w = x.parent.right
282+
if w.color == Node::RED
283+
w.color = Node::BLACK
284+
x.parent.color = Node::RED
285+
left_rotate(x.parent)
286+
w = x.parent.right
287+
end
288+
if w.left.color == Node::BLACK && w.right.color == Node::BLACK
289+
w.color = Node::RED
290+
x = x.parent
291+
else
292+
if w.right.color == Node::BLACK
293+
w.left.color = Node::BLACK
294+
w.color = Node::RED
295+
right_rotate(w)
296+
w = x.parent.right
297+
end
298+
w.color = x.parent.color
299+
x.parent.color = Node::BLACK
300+
w.right.color = Node::BLACK
301+
left_rotate(x.parent)
302+
x = root
303+
end
304+
else
305+
w = x.parent.left
306+
if w.color == Node::RED
307+
w.color = Node::BLACK
308+
x.parent.color = Node::RED
309+
right_rotate(x.parent)
310+
w = x.parent.left
311+
end
312+
if w.right.color == Node::BLACK && w.left.color == Node::BLACK
313+
w.color = Node::RED
314+
x = x.parent
315+
else
316+
if w.left.color == Node::BLACK
317+
w.right.color = Node::BLACK
318+
w.color = Node::RED
319+
left_rotate(w)
320+
w = x.parent.left
321+
end
322+
w.color = x.parent.color
323+
x.parent.color = Node::BLACK
324+
w.left.color = Node::BLACK
325+
right_rotate(x.parent)
326+
x = root
327+
end
328+
end
329+
end
330+
x.color = Node::BLACK
331+
end
332+
end
333+
334+
def rbt_bm
335+
n = 100_000
336+
a1 = []; n.times { a1 << rand(999_999) }
337+
a2 = []; n.times { a2 << rand(999_999) }
338+
339+
start = Time.now
340+
341+
tree = RedBlackTree.new
342+
343+
n.times {|i| tree.add(i) }
344+
n.times { tree.delete(tree.root) }
345+
346+
tree = RedBlackTree.new
347+
a1.each {|e| tree.add(e) }
348+
a2.each {|e| tree.search(e) }
349+
tree.inorder_walk {|key| key + 1 }
350+
tree.reverse_inorder_walk {|key| key + 1 }
351+
n.times { tree.minimum }
352+
n.times { tree.maximum }
353+
354+
return Time.now - start
355+
end
356+
357+
N = (ARGV[0] || 10).to_i
358+
359+
N.times do
360+
puts rbt_bm.to_f
361+
end

0 commit comments

Comments
 (0)