The best way to calculate the height in a binary search tree? (balancing an AVL-tree)

前端 未结 9 782
渐次进展
渐次进展 2020-12-12 10:39

I\'m looking for the best way to calculate a nodes balance in an AVL-tree. I thought I had it working, but after some heavy inserting/updating I can see that it\'s not worki

9条回答
  •  孤街浪徒
    2020-12-12 11:04

    Give BinaryTree::Node a subtreeHeight data member, initialized to 0 in its constructor, and update automatically every time with:

    template 
    inline void BinaryTree::Node::setLeft (std::shared_ptr& node) {
        const std::size_t formerLeftSubtreeSize = left ? left->subtreeSize : 0;
        left = node;
        if (node) {
            node->parent = this->shared_from_this();
            subtreeSize++;
            node->depthFromRoot = depthFromRoot + 1;
            const std::size_t h = node->subtreeHeight;
            if (right)
                subtreeHeight = std::max (right->subtreeHeight, h) + 1;
            else
                subtreeHeight = h + 1;
        }
        else {
            subtreeSize -= formerLeftSubtreeSize;
            subtreeHeight = right ? right->subtreeHeight + 1 : 0;
        }
    }
    
    template 
    inline void BinaryTree::Node::setRight (std::shared_ptr& node) {
        const std::size_t formerRightSubtreeSize = right ? right->subtreeSize : 0;
        right = node;
        if (node) {
            node->parent = this->shared_from_this();
            subtreeSize++;
            node->depthFromRoot = depthFromRoot + 1;
            const std::size_t h = node->subtreeHeight;
            if (left)
                subtreeHeight = std::max (left->subtreeHeight, h) + 1;
            else
                subtreeHeight = h + 1;
        }
        else {
            subtreeSize -= formerRightSubtreeSize;
            subtreeHeight = left ? left->subtreeHeight + 1 : 0;
        }
    }
    

    Note that data members subtreeSize and depthFromRoot are also updated. These functions are called when inserting a node (all tested), e.g.

    template 
    inline std::shared_ptr::Node>
    BinaryTree::Node::insert (BinaryTree& tree, const T& t, std::shared_ptr& node) {
        if (!node) {
            std::shared_ptr newNode = std::make_shared(tree, t);
            node = newNode;
            return newNode;
        }
        if (getComparator()(t, node->value)) {
            std::shared_ptr newLeft = insert(tree, t, node->left);
            node->setLeft(newLeft);
        }
        else {
            std::shared_ptr newRight = insert(tree, t, node->right);
            node->setRight(newRight);
        }
        return node;
    }
    

    If removing a node, use a different version of removeLeft and removeRight by replacing subtreeSize++; with subtreeSize--;. Algorithms for rotateLeft and rotateRight can be adapted without much problem either. The following was tested and passed:

    template 
    void BinaryTree::rotateLeft (std::shared_ptr& node) {  // The root of the rotation is 'node', and its right child is the pivot of the rotation.  The pivot will rotate counter-clockwise and become the new parent of 'node'.
        std::shared_ptr pivot = node->right;
        pivot->subtreeSize = node->subtreeSize;
        pivot->depthFromRoot--;
        node->subtreeSize--;  // Since 'pivot' will no longer be in the subtree rooted at 'node'.
        const std::size_t a = pivot->left ? pivot->left->subtreeHeight + 1 : 0;  // Need to establish node->heightOfSubtree before pivot->heightOfSubtree is established, since pivot->heightOfSubtree depends on it.
        node->subtreeHeight = node->left ? std::max(a, node->left->subtreeHeight + 1) : std::max(a,1);
        if (pivot->right) {
            node->subtreeSize -= pivot->right->subtreeSize;  // The subtree rooted at 'node' loses the subtree rooted at pivot->right.
            pivot->subtreeHeight = std::max (pivot->right->subtreeHeight, node->subtreeHeight) + 1;
        }
        else
            pivot->subtreeHeight = node->subtreeHeight + 1;
        node->depthFromRoot++;
        decreaseDepthFromRoot(pivot->right);  // Recursive call for the entire subtree rooted at pivot->right.
        increaseDepthFromRoot(node->left);  // Recursive call for the entire subtree rooted at node->left.
        pivot->parent = node->parent;
        if (pivot->parent) {  // pivot's new parent will be its former grandparent, which is not nullptr, so the grandparent must be updated with a new left or right child (depending on whether 'node' was its left or right child).
            if (pivot->parent->left == node)
                pivot->parent->left = pivot;
            else
                pivot->parent->right = pivot;
        }
        node->setRightSimple(pivot->left);  // Since pivot->left->value is less than pivot->value but greater than node->value.  We use the NoSizeAdjustment version because the 'subtreeSize' values of 'node' and 'pivot' are correct already.
        pivot->setLeftSimple(node);
        if (node == root) {
            root = pivot;
            root->parent = nullptr; 
        }
    }
    

    where

    inline void decreaseDepthFromRoot (std::shared_ptr& node) {adjustDepthFromRoot(node, -1);}
    inline void increaseDepthFromRoot (std::shared_ptr& node) {adjustDepthFromRoot(node, 1);}
    
    template 
    inline void BinaryTree::adjustDepthFromRoot (std::shared_ptr& node, int adjustment) {
        if (!node)
            return;
        node->depthFromRoot += adjustment;
        adjustDepthFromRoot (node->left, adjustment);
        adjustDepthFromRoot (node->right, adjustment);
    }
    

    Here is the entire code: http://ideone.com/d6arrv

提交回复
热议问题