Skip to content
Snippets Groups Projects

hlp me

  • Clone with SSH
  • Clone with HTTPS
  • Embed
  • Share
    The snippet can be accessed without any authentication.
    Authored by arn2
    btree.cpp 6.79 KiB
     * standard balanced binary search trees.
     *
     * @author Matt Joras
     * @date Winter 2013
     */
    
    using std::vector;
    
    /**
     * Finds the value associated with a given key.
     * @param key The key to look up.
     * @return The value (if found), the default V if not.
     */
    template <class K, class V>
    V BTree<K, V>::find(const K& key) const
    {
        return root == nullptr ? V() : find(root, key);
    }
    
    /**
     * Private recursive version of the find function.
     * @param subroot A reference of a pointer to the current BTreeNode.
     * @param key The key we are looking up.
     * @return The value (if found), the default V if not.
     */
    template <class K, class V>
    V BTree<K, V>::find(const BTreeNode* subroot, const K& key) const
    {
        std::cout << *subroot << std::endl;
    
        size_t first_larger_idx = insertion_idx(subroot->elements, key);
    
        /* If first_larger_idx is a valid index and the key there is the key we
         * are looking for, we are done. */
        if(first_larger_idx < subroot->elements.size() || first_larger_idx==0) {
            return subroot->elements[first_larger_idx].value;
        }
        /* Otherwise, we need to figure out which child to explore. For this we
         * can actually just use first_larger_idx directly. E.g.
         * | 1 | 5 | 7 | 8 |
         * Suppose we are looking for 6. first_larger_idx is 2. This means we want to
         * explore the child between 5 and 7. The children vector has a pointer for
         * each of the horizontal bars. The index of the horizontal bar we want is
         * 2, which is conveniently the same as first_larger_idx. If the subroot is
         * a leaf and we didn't find the key in it, then we have failed to find it
         * anywhere in the tree and return the default V.
         */
        if(subroot->is_leaf)
            return V();
        return find(subroot->children[first_larger_idx], key);
    }
    
    /**
     * Inserts a key and value into the BTree. If the key is already in the
     * tree do nothing.
     * @param key The key to insert.
     * @param value The value to insert.
     */
    template <class K, class V>
    void BTree<K, V>::insert(const K& key, const V& value)
    {
        /* Make the root node if the tree is empty. */
        if (root == nullptr) {
            root = new BTreeNode(true, order);
        }
        insert(root, DataPair(key, value));
        /* Increase height by one by tossing up one element from the old
         * root node. */
        if (root->elements.size() >= order) {
            BTreeNode* new_root = new BTreeNode(false, order);
            new_root->children.push_back(root);
            split_child(new_root, 0);
            root = new_root;
        }
    }
    
    /**
     * Splits a child node of a BTreeNode. Called if the child became too
     * large.
     * @param parent The parent whose child we are trying to split.
     * @param child_idx The index of the child in its parent's children
     * vector.
     */
    template <class K, class V>
    void BTree<K, V>::split_child(BTreeNode* parent, size_t child_idx)
    {
        /* Assume we are splitting the 3 6 8 child.
         * We want the following to happen.
         *     | 2 |
         *    /     \
         * | 1 |   | 3 | 6 | 8 |
         *
         *
         * Insert a pointer into parent's children which will point to the
         * new right node. The new right node is empty at this point.
         *     | 2 |   |
         *    /     \
         * | 1 |   | 3 | 6 | 8 |
         *
         * Insert the mid element from the child into its new position in the
         * parent's elements. At this point the median is still in the child.
         *     | 2 | 6 |
         *    /     \
         * | 1 |   | 3 | 6 | 8 |
         *
         * Now we want to copy over the elements (and children) to the right
         * of the child median into the new right node, and make sure the left
         * node only has the elements (and children) to the left of the child
         * median.
         *     | 2 | 6 |
         *    /   /     \
         * | 1 | | 3 | | 8 |
         *
         */
    
        /* The child we want to split. */
        BTreeNode* child = parent->children[child_idx];
        /* The "left" node is the old child, the right child is a new node. */
        BTreeNode* new_left = child;
        BTreeNode* new_right = new BTreeNode(child->is_leaf, order);
    
        /* E.g.
         * | 3 | 6 | 8 |
         * Mid element is at index (3 - 1) / 2 = 1 .
         * Mid child (bar) is at index 4 / 2 = 2 .
         * E.g.
         * | 2 | 4 |
         * Mid element is at index (2 - 1) / 2 = 0 .
         * Mid child (bar) is at index 2 / 2 = 1 .
         * This definition is to make this BTree act like the visualization
         * at
         * https://www.cs.usfca.edu/~galles/visualization/BTree.html
         */
        size_t mid_elem_idx = (child->elements.size() - 1) / 2;
        size_t mid_child_idx = child->children.size() / 2;
    
        /* Iterator for where we want to insert the new child. */
        auto child_itr = parent->children.begin() + child_idx + 1;
        /* Iterator for where we want to insert the new element. */
        auto elem_itr = parent->elements.begin() + child_idx;
        /* Iterator for the middle element. */
        auto mid_elem_itr = child->elements.begin() + mid_elem_idx;
        /* Iterator for the middle child. */
        auto mid_child_itr = child->children.begin() + mid_child_idx;
    
        DataPair elem = *mid_elem_itr;
    
        parent->elements.insert(elem_itr, elem);
        parent->children.insert(child_itr, new_right);
        new_right->elements.assign(mid_elem_itr + 1, new_left->elements.end());
        new_left->elements.assign(new_left->elements.begin(), mid_elem_itr);
        
        if(!new_left->is_leaf)
            new_left->children.assign(new_left->children.begin(), mid_child_itr);
        if(!new_right->is_leaf)
            new_right->children.assign(mid_child_itr, new_left->children.end());
    }
    
    /**
     * Private recursive version of the insert function.
     * @param subroot A reference of a pointer to the current BTreeNode.
     * @param pair The DataPair to be inserted.
     * Note: Original solution used std::lower_bound, but making the students
     * write an equivalent seemed more instructive.
     */
    template <class K, class V>
    void BTree<K, V>::insert(BTreeNode* subroot, const DataPair& pair)
    {
        /* There are two cases to consider.
         * If the subroot is a leaf node and the key doesn't exist subroot, we
         * should simply insert the pair into subroot.
         * Otherwise (subroot is not a leaf and the key doesn't exist in subroot)
         * we need to figure out which child to insert into and call insert on it.
         * After this call returns we need to check if the child became too large
         * and thus needs to be split to maintain order.
         */
        if(subroot == NULL)
            return;
    
    
        size_t first_larger_idx = insertion_idx(subroot->elements, pair);
        
        // Leaf node case
        if(subroot->is_leaf && !(subroot->elements[first_larger_idx] == pair)) {
            subroot->elements.insert(subroot->elements.begin() + first_larger_idx, pair);
        }
        else if(!subroot->is_leaf){
            BTreeNode* node = subroot->children[first_larger_idx];
            this->insert(node, pair);
            // did insertion break the tree?
    	if(node->elements.size() >= order)
                split_child(subroot, first_larger_idx);
        }
    
    }
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Finish editing this message first!
    Please register or to comment