Permalink
Browse files

Adopt Left leaning RBTree

I was unaware that LL RBTree is faster in addition to it's simpler.  See
more detail at http://www.cs.princeton.edu/~rs/talks/LLRB/LLRB.pdf
Trees are rebalanced in 'left leaning' manner after insert so you don't
see '-' as results of dump_sexp (See updated test expectations.)

Thanks Gaku Nakamura-san for letting me know this by implementing Scala
version of RBTreeMap!
  • Loading branch information...
1 parent b784697 commit c247efbf7f4e29a91b9fc07b9bed70900cad6aea @nahi nahi committed Mar 28, 2012
Showing with 48 additions and 52 deletions.
  1. +40 −44 lib/rbtree_map.rb
  2. +8 −8 test/test_rbtree_map.rb
View
@@ -27,15 +27,14 @@ def insert(key, value)
case key <=> @key
when -1
@left = @left.insert(key, value)
- new_node = rebalance_for_left_insert
when 0
@value = value
- new_node = self
when 1
@right = @right.insert(key, value)
- new_node = rebalance_for_right_insert
end
- new_node.pullup_red
+ # Rebalance of Left leaning red-black trees
+ # http://www.cs.princeton.edu/~rs/talks/LLRB/LLRB.pdf
+ insert_rotate_left.insert_rotate_right.insert_color_flip
end
# returns value
@@ -55,7 +54,7 @@ def retrieve(key)
end
def height
- [@left.height, @right.height].max + (black? ? 1 : 0)
+ @left.height + (black? ? 1 : 0)
end
class EmptyTree < RBTree
@@ -83,14 +82,41 @@ def height
protected
- def need_rebalance?
- red? and (@right.red? or @left.red?)
+ # Left leaning RBTree rebalancing methods after insert
+
+ # Do roate_left after insert if needed
+ def insert_rotate_left
+ if @left.black? and @right.red?
+ rotate_left
+ else
+ self
+ end
+ end
+
+ # Do rotate_right after insert if needed
+ def insert_rotate_right
+ if @left.red? and @left.left.red?
+ rotate_right
+ else
+ self
+ end
end
- def color_flip(other)
+ # Do color_flipt after insert if needed
+ def insert_color_flip
+ if @left.red? and @right.red?
+ color_flip
+ else
+ self
+ end
+ end
+
+ def swap_color(other)
@color, other.color = other.color, @color
end
+ private
+
# Right single rotation
# (b a (D c E)) where D and E are RED --> (d (B a c) E)
#
@@ -104,7 +130,7 @@ def rotate_left
root = @right
@right = root.left
root.left = self
- root.color_flip(root.left)
+ root.swap_color(root.left)
root
end
@@ -121,52 +147,22 @@ def rotate_right
root = @left
@left = root.right
root.right = self
- root.color_flip(root.right)
+ root.swap_color(root.right)
root
end
- # Pull up red nodes
+ # Flip colors between red children and black parent
# (b (A C)) where A and C are RED --> (B (a c))
#
# b B
# / \ -> / \
# A C a c
#
- def pullup_red
- if black? and @left.red? and @right.red?
- @left.color = @right.color = :BLACK
- self.color = :RED
- end
+ def color_flip
+ @left.color = @right.color = :BLACK
+ self.color = :RED
self
end
-
- private
-
- # trying to rebalance when the left sub-tree is 1 level higher than the right
- def rebalance_for_left_insert
- if black? and @left.need_rebalance?
- # move 1 black from the left to the right by single/double rotation
- if @left.right.red?
- @left = @left.rotate_left
- end
- rotate_right
- else
- self
- end
- end
-
- # trying to rebalance when the right sub-tree is 1 level higher than the left
- def rebalance_for_right_insert
- if black? and @right.need_rebalance?
- # move 1 black from the right to the left by single/double rotation
- if @right.left.red?
- @right = @right.rotate_right
- end
- rotate_left
- else
- self
- end
- end
end
class << self
View
@@ -36,13 +36,13 @@ def test_tree_rotate_RR
h.put('a', 1)
assert_equal 'a', h.dump_sexp
h.put('b', 2)
- assert_equal '(a - b)', h.dump_sexp
+ assert_equal '(b a)', h.dump_sexp
h.put('c', 3)
assert_equal '(b a c)', h.dump_sexp
h.put('d', 4)
- assert_equal '(b a (c - d))', h.dump_sexp
+ assert_equal '(b a (d c))', h.dump_sexp
h.put('e', 5)
- assert_equal '(b a (d c e))', h.dump_sexp
+ assert_equal '(d (b a c) e)', h.dump_sexp
end
def test_tree_rotate_LL
@@ -65,13 +65,13 @@ def test_tree_rotate_RL
h.put('g', 3)
h.put('d', 4)
h.put('h', 5)
- assert_equal '(b a (g d h))', h.dump_sexp
+ assert_equal '(g (b a d) h)', h.dump_sexp
h.put('c', 6)
- assert_equal '(b a (g (d c) h))', h.dump_sexp
+ assert_equal '(g (b a (d c)) h)', h.dump_sexp
h.put('e', 6)
assert_equal '(d (b a c) (g e h))', h.dump_sexp
h.put('f', 6)
- assert_equal '(d (b a c) (g (e - f) h))', h.dump_sexp
+ assert_equal '(d (b a c) (g (f e) h))', h.dump_sexp
end
def test_tree_rotate_LR
@@ -85,9 +85,9 @@ def test_tree_rotate_LR
h.put('0', 7)
h.put('c', 8)
h.put('e', 9)
- assert_equal '(d (b (a 0) c) (g e (h - i)))', h.dump_sexp
+ assert_equal '(d (b (a 0) c) (g e (i h)))', h.dump_sexp
h.put('f', 10)
- assert_equal '(d (b (a 0) c) (g (e - f) (h - i)))', h.dump_sexp
+ assert_equal '(d (b (a 0) c) (g (f e) (i h)))', h.dump_sexp
end
def test_aref_nil

0 comments on commit c247efb

Please sign in to comment.