Sans Pareil Technologies, Inc.

Key To Your Business

Balanced Binary Tree


We will modify the binary search tree developed in Lab 1 to add a simple on demand balancing feature. To balance the tree, we will use a simple yet efficient technique - perform an in-order traversal of the tree and move all the nodes into a std::vector, and then rebuild the tree starting at the middle of the vector and going down at the middle of each half.
BinarySearchTree.h 
We will add a public balance function to the tree. We will delegate to internal (private) flatten and buildTree functions that will convert the tree into a vector, and then rebuild the tree from the vector.
  public:
    void balance();

  private:
    void flatten( typename Node::Ptr& node, Vector& vector );
    auto buildTree( Vector& vector, const std::size_t start, const std::size_t end ) -> typename Node::Ptr;
BSTImpl.h 
The implementation of the functions added to the tree definition are kept in the include file to aid readability of the tree interface.
template<typename Data, typename Comparator>
void BinarySearchTree<Data, Comparator>::balance()
{
  if ( ! rootNode ) throw std::out_of_range( "Empty tree!" );
  if ( count == 1 ) return;

  Vector vector;
  vector.reserve( count );
  flatten( rootNode, vector );
  rootNode = buildTree( vector, 0, count - 1 );
}

template<typename Data, typename Comparator>
void BinarySearchTree<Data, Comparator>::flatten( typename Node::Ptr& node, Vector& vector )
{
  if ( node->left ) flatten( node->left, vector );
  vector.emplace_back( std::move( node ) );
  if ( vector.back()->right ) flatten( vector.back()->right, vector );
}

template <typename Data, typename Comparator>
auto BinarySearchTree<Data, Comparator>::buildTree( Vector& vector, const std::size_t start,
  const std::size_t end ) -> typename Node::Ptr
{
  if ( start > end ) return nullptr;
  const auto middle = start + ( end - start ) / 2;
  auto node = std::move( vector[middle] );
  if ( middle ) node->left = buildTree( vector, start, middle - 1 );
  node->right = buildTree( vector, middle + 1, end );
  return std::move( node );
}
simple.cpp 
We will add a very rudimentary test to make sure that tree balancing works. We will look for an expected new root node value, as well as ensure that in-order traversal of the tree produces the same original output.
SCENARIO( "An unbalanced BST may be balanced" )
{
  GIVEN( "An unbalanced BST of integers" )
  {
    csc280::BinarySearchTree<uint16_t> bst{ 1, 2, 3, 4, 5 };

    WHEN( "The bst is balanced" )
    {
      bst.balance();
      REQUIRE( 3 == bst.root() );
    }

    AND_WHEN( "Visited in-order" )
    {
      csc280::internal::StringVisitor<uint16_t> visitor;
      bst.visitInorder( visitor );
      REQUIRE( "1,2,3,4,5," == visitor.ss.str() );
    }
  }
}