From c1fdae5eb46ccddc08c0666cac069e63ff1091a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ion=20Gazta=C3=B1aga?= Date: Thu, 6 Feb 2014 11:10:47 +0100 Subject: [PATCH] Optimized tree-rebalancing code to avoid redundant pointer updates. --- doc/intrusive.qbk | 2 + .../boost/intrusive/avltree_algorithms.hpp | 207 +++++++---- include/boost/intrusive/bstree_algorithms.hpp | 351 +++++++++--------- include/boost/intrusive/rbtree_algorithms.hpp | 144 +++---- .../boost/intrusive/splaytree_algorithms.hpp | 8 +- include/boost/intrusive/treap_algorithms.hpp | 43 +-- 6 files changed, 389 insertions(+), 366 deletions(-) diff --git a/doc/intrusive.qbk b/doc/intrusive.qbk index f1dba96..a7b15ac 100644 --- a/doc/intrusive.qbk +++ b/doc/intrusive.qbk @@ -3797,6 +3797,8 @@ to be inserted in intrusive containers are allocated using `std::vector` or `std * Fixed bugs: * [@https://svn.boost.org/trac/boost/ticket/9332 #9332: ['"has_member_function_callable_with.hpp compile error on msvc-12.0"]]. +* Optimized tree rebalancing code to avoid redundant assignments. + [endsect] [section:release_notes_boost_1_55_00 Boost 1.55 Release] diff --git a/include/boost/intrusive/avltree_algorithms.hpp b/include/boost/intrusive/avltree_algorithms.hpp index 31777d7..e6127b8 100644 --- a/include/boost/intrusive/avltree_algorithms.hpp +++ b/include/boost/intrusive/avltree_algorithms.hpp @@ -49,15 +49,6 @@ struct avltree_node_cloner } }; -template -struct avltree_erase_fixup -{ - typedef typename NodeTraits::node_ptr node_ptr; - - void operator()(const node_ptr & to_erase, const node_ptr & successor) - { NodeTraits::set_balance(successor, NodeTraits::get_balance(to_erase)); } -}; - /// @endcond //! avltree_algorithms is configured with a NodeTraits class, which encapsulates the @@ -225,7 +216,10 @@ class avltree_algorithms static node_ptr erase(const node_ptr & header, const node_ptr & z) { typename bstree_algo::data_for_rebalance info; - bstree_algo::erase(header, z, avltree_erase_fixup(), info); + bstree_algo::erase(header, z, info); + if(info.y != z){ + NodeTraits::set_balance(info.y, NodeTraits::get_balance(z)); + } //Rebalance avltree rebalance_after_erasure(header, info.x, info.x_parent); return z; @@ -358,41 +352,92 @@ class avltree_algorithms /// @cond + static bool verify(const node_ptr &header) + { + std::size_t height; + std::size_t count; + return verify_recursion(NodeTraits::get_parent(header), count, height); + } + private: - static void rebalance_after_erasure(const node_ptr & header, const node_ptr & xnode, const node_ptr & xnode_parent) + static bool verify_recursion(node_ptr n, std::size_t &count, std::size_t &height) + { + if (!n){ + count = 0; + height = 0; + return true; + } + std::size_t leftcount, rightcount; + std::size_t leftheight, rightheight; + if(!verify_recursion(NodeTraits::get_left (n), leftcount, leftheight) || + !verify_recursion(NodeTraits::get_right(n), rightcount, rightheight) ){ + return false; + } + count = 1u + leftcount + rightcount; + height = 1u + (leftheight > rightheight ? leftheight : rightheight); + + //If equal height, balance must be zero + if(rightheight == leftheight){ + if(NodeTraits::get_balance(n) != NodeTraits::zero()){ + BOOST_ASSERT(0); + return false; + } + } + //If right is taller than left, then the difference must be at least 1 and the balance positive + else if(rightheight > leftheight){ + if(rightheight - leftheight > 1 ){ + BOOST_ASSERT(0); + return false; + } + else if(NodeTraits::get_balance(n) != NodeTraits::positive()){ + BOOST_ASSERT(0); + return false; + } + } + //If left is taller than right, then the difference must be at least 1 and the balance negative + else{ + if(leftheight - rightheight > 1 ){ + BOOST_ASSERT(0); + return false; + } + else if(NodeTraits::get_balance(n) != NodeTraits::negative()){ + BOOST_ASSERT(0); + return false; + } + } + return true; + } + + static void rebalance_after_erasure(const node_ptr & header, node_ptr x, node_ptr x_parent) { - node_ptr x(xnode), x_parent(xnode_parent); for (node_ptr root = NodeTraits::get_parent(header); x != root; root = NodeTraits::get_parent(header)) { const balance x_parent_balance = NodeTraits::get_balance(x_parent); - node_ptr const x_parent_left(NodeTraits::get_left(x_parent)); + //Don't cache x_is_leftchild or similar because x can be null and + //equal to both x_parent_left and x_parent_right + const node_ptr x_parent_left (NodeTraits::get_left(x_parent)); + const node_ptr x_parent_right(NodeTraits::get_right(x_parent)); + if(x_parent_balance == NodeTraits::zero()){ - NodeTraits::set_balance( x_parent - , x == x_parent_left ? NodeTraits::positive() : NodeTraits::negative() ); + NodeTraits::set_balance( x_parent, x == x_parent_right ? NodeTraits::negative() : NodeTraits::positive() ); break; // the height didn't change, let's stop here } else if(x_parent_balance == NodeTraits::negative()){ - if (x == x_parent_left) { + if (x == x_parent_left) { ////x is left child or x and sibling are null NodeTraits::set_balance(x_parent, NodeTraits::zero()); // balanced x = x_parent; - x_parent = NodeTraits::get_parent(x_parent); } else { - // x is right child - // a is left child - node_ptr a = x_parent_left; - BOOST_INTRUSIVE_INVARIANT_ASSERT(a); - if (NodeTraits::get_balance(a) == NodeTraits::positive()) { - // a MUST have a right child - BOOST_INTRUSIVE_INVARIANT_ASSERT(NodeTraits::get_right(a)); - rotate_left_right(x_parent, header); - x = NodeTraits::get_parent(x_parent); - x_parent = NodeTraits::get_parent(x); + // x is right child (x_parent_left is the left child) + BOOST_INTRUSIVE_INVARIANT_ASSERT(x_parent_left); + if (NodeTraits::get_balance(x_parent_left) == NodeTraits::positive()) { + // x_parent_left MUST have a right child + BOOST_INTRUSIVE_INVARIANT_ASSERT(NodeTraits::get_right(x_parent_left)); + x = avl_rotate_left_right(x_parent, x_parent_left, header); } else { - rotate_right(x_parent, header); - x = NodeTraits::get_parent(x_parent); - x_parent = NodeTraits::get_parent(x); + avl_rotate_right(x_parent, x_parent_left, header); + x = x_parent_left; } // if changed from negative to NodeTraits::positive(), no need to check above @@ -402,28 +447,22 @@ class avltree_algorithms } } else if(x_parent_balance == NodeTraits::positive()){ - if (x == NodeTraits::get_right(x_parent)) { + if (x == x_parent_right) { //x is right child or x and sibling are null NodeTraits::set_balance(x_parent, NodeTraits::zero()); // balanced x = x_parent; - x_parent = NodeTraits::get_parent(x_parent); } else { - // x is left child - // a is right child - node_ptr a = NodeTraits::get_right(x_parent); - BOOST_INTRUSIVE_INVARIANT_ASSERT(a); - if (NodeTraits::get_balance(a) == NodeTraits::negative()) { - // a MUST have then a left child - BOOST_INTRUSIVE_INVARIANT_ASSERT(NodeTraits::get_left(a)); - rotate_right_left(x_parent, header); - - x = NodeTraits::get_parent(x_parent); - x_parent = NodeTraits::get_parent(x); + // x is left child (x_parent_right is the right child) + const node_ptr x_parent_right(NodeTraits::get_right(x_parent)); + BOOST_INTRUSIVE_INVARIANT_ASSERT(x_parent_right); + if (NodeTraits::get_balance(x_parent_right) == NodeTraits::negative()) { + // x_parent_right MUST have then a left child + BOOST_INTRUSIVE_INVARIANT_ASSERT(NodeTraits::get_left(x_parent_right)); + x = avl_rotate_right_left(x_parent, x_parent_right, header); } else { - rotate_left(x_parent, header); - x = NodeTraits::get_parent(x_parent); - x_parent = NodeTraits::get_parent(x); + avl_rotate_left(x_parent, x_parent_right, header); + x = x_parent_right; } // if changed from NodeTraits::positive() to negative, no need to check above if (NodeTraits::get_balance(x) == NodeTraits::negative()){ @@ -434,44 +473,44 @@ class avltree_algorithms else{ BOOST_INTRUSIVE_INVARIANT_ASSERT(false); // never reached } + x_parent = NodeTraits::get_parent(x); } } - static void rebalance_after_insertion(const node_ptr & header, const node_ptr & xnode) + static void rebalance_after_insertion(const node_ptr & header, node_ptr x) { - node_ptr x(xnode); NodeTraits::set_balance(x, NodeTraits::zero()); // Rebalance. for(node_ptr root = NodeTraits::get_parent(header); x != root; root = NodeTraits::get_parent(header)){ node_ptr const x_parent(NodeTraits::get_parent(x)); node_ptr const x_parent_left(NodeTraits::get_left(x_parent)); const balance x_parent_balance = NodeTraits::get_balance(x_parent); + const bool x_is_leftchild(x == x_parent_left); if(x_parent_balance == NodeTraits::zero()){ // if x is left, parent will have parent->bal_factor = negative // else, parent->bal_factor = NodeTraits::positive() - NodeTraits::set_balance( x_parent, x == x_parent_left - ? NodeTraits::negative() : NodeTraits::positive() ); + NodeTraits::set_balance( x_parent, x_is_leftchild ? NodeTraits::negative() : NodeTraits::positive() ); x = x_parent; } else if(x_parent_balance == NodeTraits::positive()){ // if x is a left child, parent->bal_factor = zero - if (x == x_parent_left) + if (x_is_leftchild) NodeTraits::set_balance(x_parent, NodeTraits::zero()); else{ // x is a right child, needs rebalancing if (NodeTraits::get_balance(x) == NodeTraits::negative()) - rotate_right_left(x_parent, header); + avl_rotate_right_left(x_parent, x, header); else - rotate_left(x_parent, header); + avl_rotate_left(x_parent, x, header); } break; } else if(x_parent_balance == NodeTraits::negative()){ // if x is a left child, needs rebalancing - if (x == x_parent_left) { + if (x_is_leftchild) { if (NodeTraits::get_balance(x) == NodeTraits::positive()) - rotate_left_right(x_parent, header); + avl_rotate_left_right(x_parent, x, header); else - rotate_right(x_parent, header); + avl_rotate_right(x_parent, x, header); } else NodeTraits::set_balance(x_parent, NodeTraits::zero()); @@ -508,8 +547,8 @@ class avltree_algorithms } } - static void rotate_left_right(const node_ptr a, const node_ptr & hdr) - { + static node_ptr avl_rotate_left_right(const node_ptr a, const node_ptr a_oldleft, const node_ptr & hdr) + { // [note: 'a_oldleft' is 'b'] // | | // // a(-2) c // // / \ / \ // @@ -517,16 +556,19 @@ class avltree_algorithms // (pos)b [g] b a // // / \ / \ / \ // // [d] c [d] e f [g] // - // / \ // - // e f // - node_ptr b = NodeTraits::get_left(a), c = NodeTraits::get_right(b); - bstree_algo::rotate_left(b, hdr); - bstree_algo::rotate_right(a, hdr); - left_right_balancing(a, b, c); + // / \ // + // e f // + const node_ptr c = NodeTraits::get_right(a_oldleft); + bstree_algo::rotate_left_no_parent_fix(a_oldleft, c); + //No need to link c with a [NodeTraits::set_parent(c, a) + NodeTraits::set_left(a, c)] + //as c is not root and another rotation is coming + bstree_algo::rotate_right(a, c, NodeTraits::get_parent(a), hdr); + left_right_balancing(a, a_oldleft, c); + return c; } - static void rotate_right_left(const node_ptr a, const node_ptr & hdr) - { + static node_ptr avl_rotate_right_left(const node_ptr a, const node_ptr a_oldright, const node_ptr & hdr) + { // [note: 'a_oldright' is 'b'] // | | // // a(pos) c // // / \ / \ // @@ -536,41 +578,42 @@ class avltree_algorithms // c [g] [d] e f [g] // // / \ // // e f // - node_ptr b = NodeTraits::get_right(a), c = NodeTraits::get_left(b); - bstree_algo::rotate_right(b, hdr); - bstree_algo::rotate_left(a, hdr); - left_right_balancing(b, a, c); + const node_ptr c (NodeTraits::get_left(a_oldright)); + bstree_algo::rotate_right_no_parent_fix(a_oldright, c); + //No need to link c with a [NodeTraits::set_parent(c, a) + NodeTraits::set_right(a, c)] + //as c is not root and another rotation is coming. + bstree_algo::rotate_left(a, c, NodeTraits::get_parent(a), hdr); + left_right_balancing(a_oldright, a, c); + return c; } - static void rotate_left(const node_ptr x, const node_ptr & hdr) + static void avl_rotate_left(const node_ptr &x, const node_ptr &x_oldright, const node_ptr & hdr) { - const node_ptr y = NodeTraits::get_right(x); - bstree_algo::rotate_left(x, hdr); + bstree_algo::rotate_left(x, x_oldright, NodeTraits::get_parent(x), hdr); // reset the balancing factor - if (NodeTraits::get_balance(y) == NodeTraits::positive()) { + if (NodeTraits::get_balance(x_oldright) == NodeTraits::positive()) { NodeTraits::set_balance(x, NodeTraits::zero()); - NodeTraits::set_balance(y, NodeTraits::zero()); + NodeTraits::set_balance(x_oldright, NodeTraits::zero()); } else { // this doesn't happen during insertions NodeTraits::set_balance(x, NodeTraits::positive()); - NodeTraits::set_balance(y, NodeTraits::negative()); + NodeTraits::set_balance(x_oldright, NodeTraits::negative()); } } - static void rotate_right(const node_ptr x, const node_ptr & hdr) + static void avl_rotate_right(const node_ptr &x, const node_ptr &x_oldleft, const node_ptr & hdr) { - const node_ptr y = NodeTraits::get_left(x); - bstree_algo::rotate_right(x, hdr); + bstree_algo::rotate_right(x, x_oldleft, NodeTraits::get_parent(x), hdr); // reset the balancing factor - if (NodeTraits::get_balance(y) == NodeTraits::negative()) { + if (NodeTraits::get_balance(x_oldleft) == NodeTraits::negative()) { NodeTraits::set_balance(x, NodeTraits::zero()); - NodeTraits::set_balance(y, NodeTraits::zero()); + NodeTraits::set_balance(x_oldleft, NodeTraits::zero()); } else { // this doesn't happen during insertions NodeTraits::set_balance(x, NodeTraits::negative()); - NodeTraits::set_balance(y, NodeTraits::positive()); + NodeTraits::set_balance(x_oldleft, NodeTraits::positive()); } } diff --git a/include/boost/intrusive/bstree_algorithms.hpp b/include/boost/intrusive/bstree_algorithms.hpp index e7f034b..0a7cef0 100644 --- a/include/boost/intrusive/bstree_algorithms.hpp +++ b/include/boost/intrusive/bstree_algorithms.hpp @@ -549,7 +549,7 @@ class bstree_algorithms //! Complexity: Logarithmic to the size of the subtree. //! //! Throws: Nothing. - static node_ptr minimum (node_ptr node) + static node_ptr minimum(node_ptr node) { for(node_ptr p_left = NodeTraits::get_left(node) ;p_left @@ -1062,16 +1062,12 @@ class bstree_algorithms //Since we've found the upper bound there is no other value with the same key if: // - There is no previous node // - The previous node is less than the key - if(!prev || comp(prev, key)){ + const bool not_present = !prev || comp(prev, key); + if(not_present){ commit_data.link_left = left_child; commit_data.node = y; - return std::pair(node_ptr(), true); - } - //If the previous value was not less than key, it means that it's equal - //(because we've checked the upper bound) - else{ - return std::pair(prev, false); } + return std::pair(prev, not_present); } //! Requires: "header" must be the header node of a tree. @@ -1362,7 +1358,7 @@ class bstree_algorithms static void erase(const node_ptr & header, const node_ptr & z) { data_for_rebalance ignored; - erase_impl(header, z, ignored); + erase(header, z, ignored); } //! Requires: node is a tree node but not the header. @@ -1447,6 +1443,88 @@ class bstree_algorithms } protected: + static void erase(const node_ptr & header, const node_ptr & z, data_for_rebalance &info) + { + node_ptr y(z); + node_ptr x; + const node_ptr z_left(NodeTraits::get_left(z)); + const node_ptr z_right(NodeTraits::get_right(z)); + + if(!z_left){ + x = z_right; // x might be null. + } + else if(!z_right){ // z has exactly one non-null child. y == z. + x = z_left; // x is not null. + BOOST_ASSERT(x); + } + else{ //make y != z + // y = find z's successor + y = bstree_algorithms::minimum(z_right); + x = NodeTraits::get_right(y); // x might be null. + } + + node_ptr x_parent; + const node_ptr z_parent(NodeTraits::get_parent(z)); + const bool z_is_leftchild(NodeTraits::get_left(z_parent) == z); + + if(y != z){ //has two children and y is the minimum of z + //y is z's successor and it has a null left child. + //x is the right child of y (it can be null) + //Relink y in place of z and link x with y's old parent + NodeTraits::set_parent(z_left, y); + NodeTraits::set_left(y, z_left); + if(y != z_right){ + //Link y with the right tree of z + NodeTraits::set_right(y, z_right); + NodeTraits::set_parent(z_right, y); + //Link x with y's old parent (y must be a left child) + x_parent = NodeTraits::get_parent(y); + BOOST_ASSERT(NodeTraits::get_left(x_parent) == y); + if(x) + NodeTraits::set_parent(x, x_parent); + //Since y was the successor and not the right child of z, it must be a left child + NodeTraits::set_left(x_parent, x); + } + else{ //y was the right child of y so no need to fix x's position + x_parent = y; + } + NodeTraits::set_parent(y, z_parent); + bstree_algorithms::set_child(header, y, z_parent, z_is_leftchild); + } + else { // z has zero or one child, x is one child (it can be null) + //Just link x to z's parent + x_parent = z_parent; + if(x) + NodeTraits::set_parent(x, z_parent); + bstree_algorithms::set_child(header, x, z_parent, z_is_leftchild); + + //Now update leftmost/rightmost in case z was one of them + if(NodeTraits::get_left(header) == z){ + //z_left must be null because z is the leftmost + BOOST_ASSERT(!z_left); + NodeTraits::set_left(header, !z_right ? + z_parent : // makes leftmost == header if z == root + bstree_algorithms::minimum(z_right)); + } + if(NodeTraits::get_right(header) == z){ + //z_right must be null because z is the rightmost + BOOST_ASSERT(!z_right); + NodeTraits::set_right(header, !z_left ? + z_parent : // makes rightmost == header if z == root + bstree_algorithms::maximum(z_left)); + } + } + + //If z had 0/1 child, y == z and one of its children (and maybe null) + //If z had 2 children, y is the successor of z and x is the right child of y + info.x = x; + info.y = y; + //If z had 0/1 child, x_parent is the new parent of the old right child of y (z's successor) + //If z had 2 children, x_parent is the new parent of y (z_parent) + BOOST_ASSERT(!x || NodeTraits::get_parent(x) == x_parent); + info.x_parent = x_parent; + } + //! Requires: node is a node of the tree but it's not the header. //! //! Effects: Returns the number of nodes of the subtree. @@ -1510,83 +1588,6 @@ class bstree_algorithms static bool is_right_child(const node_ptr & p) { return NodeTraits::get_right(NodeTraits::get_parent(p)) == p; } - template - static void erase(const node_ptr & header, const node_ptr & z, F z_and_successor_fixup, data_for_rebalance &info) - { - erase_impl(header, z, info); - if(info.y != z){ - z_and_successor_fixup(z, info.y); - } - } - - //Fix header and own's parent data when replacing x with own, providing own's old data with parent - static void replace_own_impl(const node_ptr & own, const node_ptr & x, const node_ptr & header, const node_ptr & own_parent, bool own_was_left) - { - if(NodeTraits::get_parent(header) == own) - NodeTraits::set_parent(header, x); - else if(own_was_left) - NodeTraits::set_left(own_parent, x); - else - NodeTraits::set_right(own_parent, x); - } - - //Fix header and own's parent data when replacing x with own, supposing own - //links with its parent are still ok - static void replace_own(const node_ptr & own, const node_ptr & x, const node_ptr & header) - { - node_ptr own_parent(NodeTraits::get_parent(own)); - bool own_is_left(NodeTraits::get_left(own_parent) == own); - replace_own_impl(own, x, header, own_parent, own_is_left); - } - - // rotate parent p to left (no header and p's parent fixup) - static node_ptr rotate_left(const node_ptr & p) - { - node_ptr x(NodeTraits::get_right(p)); - node_ptr x_left(NodeTraits::get_left(x)); - NodeTraits::set_right(p, x_left); - if(x_left){ - NodeTraits::set_parent(x_left, p); - } - NodeTraits::set_left(x, p); - NodeTraits::set_parent(p, x); - return x; - } - - // rotate parent p to left (with header and p's parent fixup) - static void rotate_left(const node_ptr & p, const node_ptr & header) - { - bool p_was_left(is_left_child(p)); - node_ptr p_old_parent(NodeTraits::get_parent(p)); - node_ptr x(rotate_left(p)); - NodeTraits::set_parent(x, p_old_parent); - replace_own_impl(p, x, header, p_old_parent, p_was_left); - } - - // rotate parent p to right (no header and p's parent fixup) - static node_ptr rotate_right(const node_ptr & p) - { - node_ptr x(NodeTraits::get_left(p)); - node_ptr x_right(NodeTraits::get_right(x)); - NodeTraits::set_left(p, x_right); - if(x_right){ - NodeTraits::set_parent(x_right, p); - } - NodeTraits::set_right(x, p); - NodeTraits::set_parent(p, x); - return x; - } - - // rotate parent p to right (with header and p's parent fixup) - static void rotate_right(const node_ptr & p, const node_ptr & header) - { - bool p_was_left(is_left_child(p)); - node_ptr p_old_parent(NodeTraits::get_parent(p)); - node_ptr x(rotate_right(p)); - NodeTraits::set_parent(x, p_old_parent); - replace_own_impl(p, x, header, p_old_parent, p_was_left); - } - static void insert_before_check (const node_ptr &header, const node_ptr & pos , insert_commit_data &commit_data @@ -1668,12 +1669,40 @@ class bstree_algorithms template static void insert_equal_upper_bound_check (const node_ptr & h, const node_ptr & new_node, NodePtrCompare comp, insert_commit_data & commit_data, std::size_t *pdepth = 0) - { insert_equal_check_impl(true, h, new_node, comp, commit_data, pdepth); } + { + std::size_t depth = 0; + node_ptr y(h); + node_ptr x(NodeTraits::get_parent(y)); + + while(x){ + ++depth; + y = x; + x = comp(new_node, x) ? + NodeTraits::get_left(x) : NodeTraits::get_right(x); + } + if(pdepth) *pdepth = depth; + commit_data.link_left = (y == h) || comp(new_node, y); + commit_data.node = y; + } template static void insert_equal_lower_bound_check (const node_ptr & h, const node_ptr & new_node, NodePtrCompare comp, insert_commit_data & commit_data, std::size_t *pdepth = 0) - { insert_equal_check_impl(false, h, new_node, comp, commit_data, pdepth); } + { + std::size_t depth = 0; + node_ptr y(h); + node_ptr x(NodeTraits::get_parent(y)); + + while(x){ + ++depth; + y = x; + x = !comp(x, new_node) ? + NodeTraits::get_left(x) : NodeTraits::get_right(x); + } + if(pdepth) *pdepth = depth; + commit_data.link_left = (y == h) || !comp(y, new_node); + commit_data.node = y; + } static void insert_commit (const node_ptr & header, const node_ptr & new_node, const insert_commit_data &commit_data) @@ -1701,7 +1730,61 @@ class bstree_algorithms NodeTraits::set_left(new_node, node_ptr()); } + //Fix header and own's parent data when replacing x with own, providing own's old data with parent + static void set_child(const node_ptr & header, const node_ptr & new_child, const node_ptr & new_parent, const bool link_left) + { + if(new_parent == header) + NodeTraits::set_parent(header, new_child); + else if(link_left) + NodeTraits::set_left(new_parent, new_child); + else + NodeTraits::set_right(new_parent, new_child); + } + + // rotate p to left (no header and p's parent fixup) + static void rotate_left_no_parent_fix(const node_ptr & p, const node_ptr &p_right) + { + node_ptr p_right_left(NodeTraits::get_left(p_right)); + NodeTraits::set_right(p, p_right_left); + if(p_right_left){ + NodeTraits::set_parent(p_right_left, p); + } + NodeTraits::set_left(p_right, p); + NodeTraits::set_parent(p, p_right); + } + + // rotate p to left (with header and p's parent fixup) + static void rotate_left(const node_ptr & p, const node_ptr & p_right, const node_ptr & p_parent, const node_ptr & header) + { + const bool p_was_left(NodeTraits::get_left(p_parent) == p); + rotate_left_no_parent_fix(p, p_right); + NodeTraits::set_parent(p_right, p_parent); + set_child(header, p_right, p_parent, p_was_left); + } + + // rotate p to right (no header and p's parent fixup) + static void rotate_right_no_parent_fix(const node_ptr & p, const node_ptr &p_left) + { + node_ptr p_left_right(NodeTraits::get_right(p_left)); + NodeTraits::set_left(p, p_left_right); + if(p_left_right){ + NodeTraits::set_parent(p_left_right, p); + } + NodeTraits::set_right(p_left, p); + NodeTraits::set_parent(p, p_left); + } + + // rotate p to right (with header and p's parent fixup) + static void rotate_right(const node_ptr & p, const node_ptr & p_left, const node_ptr & p_parent, const node_ptr & header) + { + const bool p_was_left(NodeTraits::get_left(p_parent) == p); + rotate_right_no_parent_fix(p, p_left); + NodeTraits::set_parent(p_left, p_parent); + set_child(header, p_left, p_parent, p_was_left); + } + private: + static void subtree_to_vine(node_ptr vine_tail, std::size_t &size) { //Inspired by LibAVL: @@ -1913,98 +1996,6 @@ class bstree_algorithms } return y; } - - - template - static void insert_equal_check_impl - (bool upper, const node_ptr & h, const node_ptr & new_node, NodePtrCompare comp, insert_commit_data & commit_data, std::size_t *pdepth = 0) - { - std::size_t depth = 0; - node_ptr y(h); - node_ptr x(NodeTraits::get_parent(y)); - bool link_left; - - if(upper){ - while(x){ - ++depth; - y = x; - x = comp(new_node, x) ? - NodeTraits::get_left(x) : NodeTraits::get_right(x); - } - link_left = (y == h) || comp(new_node, y); - } - else{ - while(x){ - ++depth; - y = x; - x = !comp(x, new_node) ? - NodeTraits::get_left(x) : NodeTraits::get_right(x); - } - link_left = (y == h) || !comp(y, new_node); - } - - commit_data.link_left = link_left; - commit_data.node = y; - if(pdepth) *pdepth = depth; - } - - static void erase_impl(const node_ptr & header, const node_ptr & z, data_for_rebalance &info) - { - node_ptr y(z); - node_ptr x; - node_ptr x_parent = node_ptr(); - node_ptr z_left(NodeTraits::get_left(z)); - node_ptr z_right(NodeTraits::get_right(z)); - if(!z_left){ - x = z_right; // x might be null. - } - else if(!z_right){ // z has exactly one non-null child. y == z. - x = z_left; // x is not null. - } - else{ - // find z's successor - y = bstree_algorithms::minimum (z_right); - x = NodeTraits::get_right(y); // x might be null. - } - - if(y != z){ - // relink y in place of z. y is z's successor - NodeTraits::set_parent(NodeTraits::get_left(z), y); - NodeTraits::set_left(y, NodeTraits::get_left(z)); - if(y != NodeTraits::get_right(z)){ - x_parent = NodeTraits::get_parent(y); - if(x) - NodeTraits::set_parent(x, x_parent); - NodeTraits::set_left(x_parent, x); // y must be a child of left_ - NodeTraits::set_right(y, NodeTraits::get_right(z)); - NodeTraits::set_parent(NodeTraits::get_right(z), y); - } - else - x_parent = y; - bstree_algorithms::replace_own (z, y, header); - NodeTraits::set_parent(y, NodeTraits::get_parent(z)); - } - else { // y == z --> z has only one child, or void - x_parent = NodeTraits::get_parent(z); - if(x) - NodeTraits::set_parent(x, x_parent); - bstree_algorithms::replace_own (z, x, header); - if(NodeTraits::get_left(header) == z){ - NodeTraits::set_left(header, !NodeTraits::get_right(z) ? // z->get_left() must be null also - NodeTraits::get_parent(z) : // makes leftmost == header if z == root - bstree_algorithms::minimum (x)); - } - if(NodeTraits::get_right(header) == z){ - NodeTraits::set_right(header, !NodeTraits::get_left(z) ? // z->get_right() must be null also - NodeTraits::get_parent(z) : // makes rightmost == header if z == root - bstree_algorithms::maximum(x)); - } - } - - info.x = x; - info.x_parent = x_parent; - info.y = y; - } }; /// @cond diff --git a/include/boost/intrusive/rbtree_algorithms.hpp b/include/boost/intrusive/rbtree_algorithms.hpp index 457df31..0c1225e 100644 --- a/include/boost/intrusive/rbtree_algorithms.hpp +++ b/include/boost/intrusive/rbtree_algorithms.hpp @@ -57,21 +57,6 @@ struct rbtree_node_cloner } }; -template -struct rbtree_erase_fixup -{ - typedef typename NodeTraits::node_ptr node_ptr; - typedef typename NodeTraits::color color; - - void operator()(const node_ptr & to_erase, const node_ptr & successor) - { - //Swap color of y and z - color tmp(NodeTraits::get_color(successor)); - NodeTraits::set_color(successor, NodeTraits::get_color(to_erase)); - NodeTraits::set_color(to_erase, tmp); - } -}; - #endif //#ifndef BOOST_INTRUSIVE_DOXYGEN_INVOKED //! rbtree_algorithms provides basic algorithms to manipulate @@ -245,10 +230,18 @@ class rbtree_algorithms static node_ptr erase(const node_ptr & header, const node_ptr & z) { typename bstree_algo::data_for_rebalance info; - bstree_algo::erase(header, z, rbtree_erase_fixup(), info); + bstree_algo::erase(header, z, info); - //Rebalance rbtree - if(NodeTraits::get_color(z) != NodeTraits::red()){ + color new_z_color; + if(info.y != z){ + new_z_color = NodeTraits::get_color(info.y); + NodeTraits::set_color(info.y, NodeTraits::get_color(z)); + } + else{ + new_z_color = NodeTraits::get_color(z); + } + //Rebalance rbtree if needed + if(new_z_color != NodeTraits::red()){ rebalance_after_erasure(header, info.x, info.x_parent); } return z; @@ -387,14 +380,20 @@ class rbtree_algorithms static void rebalance_after_erasure(const node_ptr & header, node_ptr x, node_ptr x_parent) { - while(x != NodeTraits::get_parent(header) && (!x || NodeTraits::get_color(x) == NodeTraits::black())){ - if(x == NodeTraits::get_left(x_parent)){ + while(1){ + if(x_parent == header || (x && NodeTraits::get_color(x) != NodeTraits::black())){ + break; + } + //Don't cache x_is_leftchild or similar because x can be null and + //equal to both x_parent_left and x_parent_right + const node_ptr x_parent_left(NodeTraits::get_left(x_parent)); + if(x == x_parent_left){ //x is left child node_ptr w = NodeTraits::get_right(x_parent); BOOST_ASSERT(w); if(NodeTraits::get_color(w) == NodeTraits::red()){ NodeTraits::set_color(w, NodeTraits::black()); NodeTraits::set_color(x_parent, NodeTraits::red()); - bstree_algo::rotate_left(x_parent, header); + bstree_algo::rotate_left(x_parent, w, NodeTraits::get_parent(x_parent), header); w = NodeTraits::get_right(x_parent); } node_ptr const w_left (NodeTraits::get_left(w)); @@ -407,26 +406,27 @@ class rbtree_algorithms } else { if(!w_right || NodeTraits::get_color(w_right) == NodeTraits::black()){ - NodeTraits::set_color(NodeTraits::get_left(w), NodeTraits::black()); + NodeTraits::set_color(w_left, NodeTraits::black()); NodeTraits::set_color(w, NodeTraits::red()); - bstree_algo::rotate_right(w, header); + bstree_algo::rotate_right(w, w_left, NodeTraits::get_parent(w), header); w = NodeTraits::get_right(x_parent); } NodeTraits::set_color(w, NodeTraits::get_color(x_parent)); NodeTraits::set_color(x_parent, NodeTraits::black()); - if(NodeTraits::get_right(w)) - NodeTraits::set_color(NodeTraits::get_right(w), NodeTraits::black()); - bstree_algo::rotate_left(x_parent, header); + const node_ptr new_wright(NodeTraits::get_right(w)); + if(new_wright) + NodeTraits::set_color(new_wright, NodeTraits::black()); + bstree_algo::rotate_left(x_parent, NodeTraits::get_right(x_parent), NodeTraits::get_parent(x_parent), header); break; } } else { // same as above, with right_ <-> left_. - node_ptr w = NodeTraits::get_left(x_parent); + node_ptr w = x_parent_left; if(NodeTraits::get_color(w) == NodeTraits::red()){ NodeTraits::set_color(w, NodeTraits::black()); NodeTraits::set_color(x_parent, NodeTraits::red()); - bstree_algo::rotate_right(x_parent, header); + bstree_algo::rotate_right(x_parent, w, NodeTraits::get_parent(x_parent), header); w = NodeTraits::get_left(x_parent); } node_ptr const w_left (NodeTraits::get_left(w)); @@ -439,16 +439,17 @@ class rbtree_algorithms } else { if(!w_left || NodeTraits::get_color(w_left) == NodeTraits::black()){ - NodeTraits::set_color(NodeTraits::get_right(w), NodeTraits::black()); + NodeTraits::set_color(w_right, NodeTraits::black()); NodeTraits::set_color(w, NodeTraits::red()); - bstree_algo::rotate_left(w, header); + bstree_algo::rotate_left(w, w_right, NodeTraits::get_parent(w), header); w = NodeTraits::get_left(x_parent); } NodeTraits::set_color(w, NodeTraits::get_color(x_parent)); NodeTraits::set_color(x_parent, NodeTraits::black()); - if(NodeTraits::get_left(w)) - NodeTraits::set_color(NodeTraits::get_left(w), NodeTraits::black()); - bstree_algo::rotate_right(x_parent, header); + const node_ptr new_wleft(NodeTraits::get_left(w)); + if(new_wleft) + NodeTraits::set_color(new_wleft, NodeTraits::black()); + bstree_algo::rotate_right(x_parent, NodeTraits::get_left(x_parent), NodeTraits::get_parent(x_parent), header); break; } } @@ -460,48 +461,49 @@ class rbtree_algorithms static void rebalance_after_insertion(const node_ptr & header, node_ptr p) { NodeTraits::set_color(p, NodeTraits::red()); - while(p != NodeTraits::get_parent(header) && NodeTraits::get_color(NodeTraits::get_parent(p)) == NodeTraits::red()){ + while(1){ node_ptr p_parent(NodeTraits::get_parent(p)); - node_ptr p_parent_parent(NodeTraits::get_parent(p_parent)); - if(bstree_algo::is_left_child(p_parent)){ - node_ptr x = NodeTraits::get_right(p_parent_parent); - if(x && NodeTraits::get_color(x) == NodeTraits::red()){ - NodeTraits::set_color(p_parent, NodeTraits::black()); - NodeTraits::set_color(p_parent_parent, NodeTraits::red()); - NodeTraits::set_color(x, NodeTraits::black()); - p = p_parent_parent; - } - else { - if(!bstree_algo::is_left_child(p)){ - p = p_parent; - bstree_algo::rotate_left(p, header); - } - node_ptr new_p_parent(NodeTraits::get_parent(p)); - node_ptr new_p_parent_parent(NodeTraits::get_parent(new_p_parent)); - NodeTraits::set_color(new_p_parent, NodeTraits::black()); - NodeTraits::set_color(new_p_parent_parent, NodeTraits::red()); - bstree_algo::rotate_right(new_p_parent_parent, header); - } + const node_ptr p_grandparent(NodeTraits::get_parent(p_parent)); + if(p_parent == header || NodeTraits::get_color(p_parent) == NodeTraits::black() || p_grandparent == header){ + break; } - else{ - node_ptr x = NodeTraits::get_left(p_parent_parent); - if(x && NodeTraits::get_color(x) == NodeTraits::red()){ - NodeTraits::set_color(p_parent, NodeTraits::black()); - NodeTraits::set_color(p_parent_parent, NodeTraits::red()); - NodeTraits::set_color(x, NodeTraits::black()); - p = p_parent_parent; - } - else{ - if(bstree_algo::is_left_child(p)){ - p = p_parent; - bstree_algo::rotate_right(p, header); + + NodeTraits::set_color(p_grandparent, NodeTraits::red()); + node_ptr const p_grandparent_left (NodeTraits::get_left (p_grandparent)); + bool const p_parent_is_left_child = p_parent == p_grandparent_left; + node_ptr const x(p_parent_is_left_child ? NodeTraits::get_right(p_grandparent) : p_grandparent_left); + + if(x && NodeTraits::get_color(x) == NodeTraits::red()){ + NodeTraits::set_color(x, NodeTraits::black()); + NodeTraits::set_color(p_parent, NodeTraits::black()); + p = p_grandparent; + } + else{ //Final step + const bool p_is_left_child(NodeTraits::get_left(p_parent) == p); + if(p_parent_is_left_child){ //p_parent is left child + if(!p_is_left_child){ //p is right child + bstree_algo::rotate_left_no_parent_fix(p_parent, p); + //No need to link p and p_grandparent: + // [NodeTraits::set_parent(p, p_grandparent) + NodeTraits::set_left(p_grandparent, p)] + //as p_grandparent is not the header, another rotation is coming and p_parent + //will be the left child of p_grandparent + p_parent = p; } - node_ptr new_p_parent(NodeTraits::get_parent(p)); - node_ptr new_p_parent_parent(NodeTraits::get_parent(new_p_parent)); - NodeTraits::set_color(new_p_parent, NodeTraits::black()); - NodeTraits::set_color(new_p_parent_parent, NodeTraits::red()); - bstree_algo::rotate_left(new_p_parent_parent, header); + bstree_algo::rotate_right(p_grandparent, p_parent, NodeTraits::get_parent(p_grandparent), header); } + else{ //p_parent is right child + if(p_is_left_child){ //p is left child + bstree_algo::rotate_right_no_parent_fix(p_parent, p); + //No need to link p and p_grandparent: + // [NodeTraits::set_parent(p, p_grandparent) + NodeTraits::set_right(p_grandparent, p)] + //as p_grandparent is not the header, another rotation is coming and p_parent + //will be the right child of p_grandparent + p_parent = p; + } + bstree_algo::rotate_left(p_grandparent, p_parent, NodeTraits::get_parent(p_grandparent), header); + } + NodeTraits::set_color(p_parent, NodeTraits::black()); + break; } } NodeTraits::set_color(NodeTraits::get_parent(header), NodeTraits::black()); diff --git a/include/boost/intrusive/splaytree_algorithms.hpp b/include/boost/intrusive/splaytree_algorithms.hpp index 313d6af..b70bd86 100644 --- a/include/boost/intrusive/splaytree_algorithms.hpp +++ b/include/boost/intrusive/splaytree_algorithms.hpp @@ -576,8 +576,8 @@ class splaytree_algorithms if(!t_left) break; if(comp(key, t_left)){ - commit.t_ = bstree_algo::rotate_right(commit.t_); - + bstree_algo::rotate_right_no_parent_fix(commit.t_, t_left); + commit.t_ = t_left; if( !NodeTraits::get_left(commit.t_) ) break; link_right(commit.t_, commit.r_); @@ -597,8 +597,8 @@ class splaytree_algorithms break; if(comp(t_right, key)){ - commit.t_ = bstree_algo::rotate_left( commit.t_ ); - + bstree_algo::rotate_left_no_parent_fix(commit.t_, t_right); + commit.t_ = t_right; if( !NodeTraits::get_right(commit.t_) ) break; link_left(commit.t_, commit.l_); diff --git a/include/boost/intrusive/treap_algorithms.hpp b/include/boost/intrusive/treap_algorithms.hpp index e7d887a..2b0704b 100644 --- a/include/boost/intrusive/treap_algorithms.hpp +++ b/include/boost/intrusive/treap_algorithms.hpp @@ -110,16 +110,17 @@ class treap_algorithms static void rotate_up_n(const node_ptr header, const node_ptr p, std::size_t n) { - for( node_ptr p_parent = NodeTraits::get_parent(p) - ; n-- - ; p_parent = NodeTraits::get_parent(p)){ - //Check if left child - if(p == NodeTraits::get_left(p_parent)){ - bstree_algo::rotate_right(p_parent, header); + node_ptr p_parent(NodeTraits::get_parent(p)); + node_ptr p_grandparent(NodeTraits::get_parent(p_parent)); + while(n--){ + if(p == NodeTraits::get_left(p_parent)){ //p is left child + bstree_algo::rotate_right(p_parent, p, p_grandparent, header); } - else{ //Right child - bstree_algo::rotate_left(p_parent, header); + else{ //p is right child + bstree_algo::rotate_left(p_parent, p, p_grandparent, header); } + p_parent = p_grandparent; + p_grandparent = NodeTraits::get_parent(p_parent); } } @@ -526,7 +527,7 @@ class treap_algorithms (const node_ptr & header, const node_ptr & new_node, const insert_commit_data &commit_data) { bstree_algo::insert_unique_commit(header, new_node, commit_data); - rebalance_after_insertion_commit(header, new_node, commit_data.rotations); + rotate_up_n(header, new_node, commit_data.rotations); } #ifdef BOOST_INTRUSIVE_DOXYGEN_INVOKED @@ -547,11 +548,12 @@ class treap_algorithms node_ptr z_left = NodeTraits::get_left(z); node_ptr z_right = NodeTraits::get_right(z); while(z_left || z_right){ + const node_ptr z_parent(NodeTraits::get_parent(z)); if(!z_right || (z_left && pcomp(z_left, z_right))){ - bstree_algo::rotate_right(z, header); + bstree_algo::rotate_right(z, z_left, z_parent, header); } else{ - bstree_algo::rotate_left(z, header); + bstree_algo::rotate_left(z, z_right, z_parent, header); } ++n; z_left = NodeTraits::get_left(z); @@ -567,10 +569,9 @@ class treap_algorithms rebalance_after_insertion_check(h, commit_data.node, new_node, pcomp, commit_data.rotations); //No-throw bstree_algo::insert_unique_commit(h, new_node, commit_data); - rebalance_after_insertion_commit(h, new_node, commit_data.rotations); + rotate_up_n(h, new_node, commit_data.rotations); } - template static void rebalance_after_insertion_check (const const_node_ptr &header, const const_node_ptr & up, const Key &k @@ -587,22 +588,6 @@ class treap_algorithms num_rotations = n; } - static void rebalance_after_insertion_commit(const node_ptr & header, const node_ptr & p, std::size_t n) - { - // Now execute n rotations - for( node_ptr p_parent = NodeTraits::get_parent(p) - ; n-- - ; p_parent = NodeTraits::get_parent(p)){ - //Check if left child - if(p == NodeTraits::get_left(p_parent)){ - bstree_algo::rotate_right(p_parent, header); - } - else{ //Right child - bstree_algo::rotate_left(p_parent, header); - } - } - } - template static bool check_invariant(const const_node_ptr & header, NodePtrPriorityCompare pcomp) {