计算具有可变限制的AVL树的平衡因子 - 可能吗?

时间:2014-04-25 11:46:03

标签: binary-tree avl-tree tree-balancing

我一直在玩代码来实现插入到AVL平衡二叉树中,这将在一个大型项目中使用。我选择了AVL而不是红黑和其他平衡方法,因为它很简单,因为我需要确信它是正确的。我的测试代码工作正常 - 但是我被引导相信可以使用可变平衡限制/容差。我没有看到这是作为AVL的一部分实现的,是的,我并不真的需要它,但作为调试的一部分,我测试的是失败的。看起来更高的平衡限制可能需要在一些旋转后向树上传播树木减少高度。

有没有其他人成功尝试过可变余额限额?注意:如果我可以提供帮助,我不想走树去重新计算平衡因子。在这个阶段,我可能会坚持平衡限制为1,除非有人有一些有用的想法。

在下面的测试代码中,如果BALANCELIMIT为1,则所有内容似乎都有效 - 但如果更高则不行

#include <stdio.h>

#define BALANCELIMIT 1      // 1 is good - anything else seems bad
#define MINI(a, b) (a<=b ? a : b)
#define MAXI(a, b) (a>=b ? a : b)

struct TREE { // Standard binary tree structure with a SIGNED balance factor (normally -1, 0, or 1)
    struct TREE *left;
    struct TREE *right;
    signed char balance;
    int key;
} *root = NULL;
int count = 0; // Keep a count of created nodes for debugging purposes

struct TREE *rotateLeft(struct TREE *r) { // Rotate left routine to replace node with node->left
    struct TREE *n = r->left;
    r->left = n->right;
    n->right = r;
    r->balance += 1 - MINI(0, n->balance); // Balance calculation sourced from:-
    n->balance += 1 + MAXI(0, r->balance); // http://oopweb.com/Algorithms/Documents/AvlTrees/Volume/AvlTrees.htm
    return n;
}

struct TREE *rotateRight(struct TREE *r) { // Rotate right routine to replace node with node->right
    struct TREE *n = r->right;
    r->right = n->left;
    n->left = r;
    r->balance += -1 - MAXI(0, n->balance); // Balance calculation sourced from:-
    n->balance += -1 + MINI(0, r->balance); // http://oopweb.com/Algorithms/Documents/AvlTrees/Volume/AvlTrees.htm
    return n;
}

int insert(struct TREE **pnode, int key) {
    struct TREE *node = *pnode;
    if (node == NULL) { // If no node then create one and initialise it to contain new value
        node = malloc(sizeof(struct TREE));
        if (node == NULL) exit(0);
        node->left = NULL;
        node->right = NULL;
        node->balance = 0;
        node->key = key;
        *pnode = node;
        count++;
        return 1;   // Recursion exit - signal tree height change (for new node)
    }

    int cmp = key - node->key;
    if (cmp == 0) return 0;
    if (cmp < 0) {  // Decide if we need to recurse to the left or to the right
        if (insert(&node->left, key)) { // For smaller item recurse left - finish unless a height change was signalled
            if (--node->balance < 0) {  // Adjust node balance - if it got worse then we need to do something...
                if (node->balance >= -BALANCELIMIT) return 1; // If height change is within limit just propagate it upward
                if (node->left->balance > 0) {              // If left path heavy to right then do a double rotation
                    printf("rotateRightLeft node %d to replace %d around %d\n", node->left->right->key, node->key, node->left->key);
                    node->left = rotateRight(node->left);   // Double rotate by first rotating left right node up one level
                    *pnode = rotateLeft(node);              // replacing left - and then rotate it again to replace current node
                } else {
                    printf("rotateLeft node %d to replace %d\n", node->left->key, node->key);
                    *pnode = rotateLeft(node);              // Left path heavy to left so just rotate left node up one level
                }
            }
        }
    } else {
        if (insert(&node->right, key)) { // For larger item recurse right - finish unless a height change was signalled
            if (++node->balance > 0) {  // Adjust node balance - if it got worse then we need to do something...
                if (node->balance <= BALANCELIMIT) return 1; // If height change is within limit just propagate it upward
                if (node->right->balance < 0) {             // If right path heavy to left then do a double rotation
                    printf("rotateLeftRight node %d to replace %d around %d\n", node->right->left->key, node->key, node->right->key);
                    node->right = rotateLeft(node->right);  // Double rotate by first rotating right left node up one level
                    *pnode = rotateRight(node);             // replacing right - and then rotate it again to replace current node
                } else {
                    printf("rotateRight node %d to replace %d\n", node->right->key, node->key);
                    *pnode = rotateRight(node);             // Right path heavy to right so just rotate right node up one level
                }
            }
        }
    }
    return 0;   // Recursion exit - signal no further action required
}

void tree_print(struct TREE *node, int depth) { // Recursive tree print routine
    if (node != NULL) {
        tree_print(node->left, depth+1);
        printf("%*.s%d (%d)\n", depth * 5, "", node->key, node->balance);
        tree_print(node->right, depth+1);
    }
}

int check_count; // node count while checking tree
int check(struct TREE *node) { // Recursive tree check routine - check count, balance factor and key order
    int lh = 0, rh = 0;
    if (node != NULL) {
        check_count++;
        if (node->left != NULL) {
            if (node->key < node->left->key) {
                printf("ERROR node key %d smaller than left node\n", node->key);
                exit(0);
            }
            lh = check(node->left); // check left subtree and get its height
        }
        if (node->right != NULL) {
            if (node->key > node->right->key) {
                printf("ERROR node key %d bigger than right node\n", node->key);
                exit(0);
            }
            rh = check(node->right); // check right subtree and get its height
        }
        if (node->balance != rh - lh) {
            printf("ERROR balance %d for %d should be %d\n", node->balance, node->key, rh-lh);
            exit(0);
        }
    }
    return MAXI(lh, rh) + 1; // Return tree height
}

void tree_check(struct TREE *r) { // wrapper function for tree check routine
    check_count = 0;
    check(r);
    if (check_count != count) {
        printf("ERROR Check count is %d not %d \n", check_count, count);
        exit(0);
    }
}

main() {
    int i;
    for (i=0; i<10; i++) insert(&root, rand() % 30000);
    for (i=0; i<10; i++) {
        int r = rand() % 30000;
        insert(&root, r);
        insert(&root, r+1);
        insert(&root, r+2);
        insert(&root, r-1);
        insert(&root, r-2);
    }
    tree_print(root, 0);
    tree_check(root);
    printf("-- Done ------- %d\n\n\n", count);
}

1 个答案:

答案 0 :(得分:0)

是的,我已成功尝试AVL树上的可变余额限制。插入后,我的 走了树,调整了平衡因子。 我也一直跟踪每个节点的高度(更多的是懒惰而不是任何东西)。

虽然您使用的公式(用于在旋转后调整降级和提升节点上的平衡因子)是正确的,但它们还不够! (当BALANCELIMIT超过1时)。旋转(应用于子树的顶部)只有在使更多平衡时才会降低子树的高度。

在双旋转的情况下,当BALANCELIMIT为1时,第一次旋转始终应用于平衡因子为+/- 1的子树顶部,并且它会切换不平衡的符号(它不会更改子树的高度,因此它不会使子树上方节点的平衡因子无效。

但是当BALANCELIMIT为2或更大时,第一次旋转(在双旋转方案中)实际上可能会降低它应用的子树的高度。最简单的例子如下所示。

-               first rotate       second rotate
- input         output             output
-  1               1                    3
-   \               \                  / \
-    4               3                1   4  
-   /               / \                \
-  3               2   4                2
- /
-2

第一次旋转(在2,3,4子树上)会出现问题。它不仅改变了编号为3和4的节点的平衡因子,因为它使(2,3,4)子树更好地平衡,它改善了节点1的平衡因子(从+3到+2)。您的代码不允许这样做。这就是它破裂的原因。要演示,请从空树开始,然后按顺序将输入1,4,3,2插入树中。第一次旋转应该改变节点1的平衡因子,但它不会。第二次轮换后,节点1的平衡系数应为1,但平衡系数的误差为2。

当旋转“意外地”改善子树高度(如上所示,当BALANCELIMIT大于1时可能发生)时,添加代码来纠正平衡因子可能是可能的,但我怀疑它会比跟踪更复杂并调整高度,并在每次旋转后重新计算平衡因子。