5

看了一些关于AVL的rebalance()功能实现的文章。
每次插入后,我们应该检查插入节点的祖先是否平衡。
所以我想,为了检查祖先的余额,我要知道插入节点的父节点。

但是,我想知道有没有其他方法可以做到这一点而不必使用父指针?
例如,节点结构:

struct Node {
    int data;
    struct Node *lchild, *rchild; //*parent;
};
4

5 回答 5

4

您可以在遍历树时维护当前节点的堆栈

stack<Node*> nodeStack;

当你遍历到一个新节点时,将它添加到堆栈中,然后你就有了你的祖先。处理完一个节点后,将其从堆栈中弹出。

** 编辑 **

对齐注释的详细说明:

struct Node {
    int data;
    struct Node *children, *parent
};

创建孩子时,这样做:

node.children = new Node[2]; or node.children = malloc(sizeof(Node) * 2);
node.children[0].parent = node;
node.children[1].parent = node;
于 2011-08-27T00:49:54.047 回答
3

使用双指针(或对 C++ 要求的指针的引用)应该完全消除对父指针的需要。

typedef struct Node {
    int value;
    int height;
    struct Node *left;
    struct Node *right;
} Node;

int height(Node *node) {
    return (node == NULL) ? -1 : node->height;
}

void insert(Node * & node, int value) {
    if (node == NULL) {
        node = new Node();
        node->value = value;
    } else if (value < node->value) {
        insert(node->left, value);
        if (height(node->left) - height(node->right) == 2) {
            if (value < note->left->value) {
                singleRotateWithLeftChild(node);
            } else {
                doubleRotateWithLeftChild(node);
            }
        }
    } else if (value > node->value) {
        // Symmetric case
    }

    node->height = 1 + max(height(node->left), height(node->right));
}
于 2011-08-27T01:05:24.083 回答
3

如果我正确地记得我的数据结构作业:

您所做的是将平衡因子存储在节点本身中作为一个 int 是:

  • -1:节点的左子树比右子树高一级(左重)
  • 0节点平衡;或者
  • 1右子树更高(右重)。

您 insert(Node subtree) 函数返回一个布尔值,如果插入使子树的高度增加,则为 true。当您从递归 insert() 调用返回时,您更新平衡因子并重新平衡树。

这可能最好用几个例子来解释:

如果当前节点处于平衡因子-1,则您正在插入子树,并且 insert(rchild) 返回true,您:

  1. 将当前节点的平衡因子更新为 0 - 左子树在插入之前较高,而右子树的高度增加了,所以现在它们的高度相同;和
  2. return false - 较浅的树的高度增加了,所以当前节点的高度保持不变

如果您要插入任一子树,并且 insert(...) 返回false

  1. 当前节点的平衡因子不变——子树高度和以前一样,平衡也一样
  2. return false - 子树高度没有改变,所以当前节点高度也没有

如果当前节点的平衡因子为0,则您将插入子树,并且 insert(lchild) 返回true

  1. 平衡因子更改为 -1 - 插入前子树的高度相同,并且插入使左侧高一
  2. 返回真

(类似地,如果插入右子树,平衡因子将变为 1。)


如果当前节点的平衡因子为-1,则您将插入子树,并且 insert(lchild) 返回true

平衡因子将变为 -2,这意味着您必须通过进行适当的旋转来重新平衡节点。我承认我对四个旋转中的每一个对平衡因子的作用以及 insert(current) 将返回的内容持空白,希望前面的示例解释了充分跟踪节点平衡的方法。

于 2011-08-27T01:22:35.207 回答
2

由于这个问题没有完整的实现,我决定添加一个。可以通过使用insert返回当前节点的递归来完成。所以,这里是代码:

typedef struct node
{
    int val;
    struct node* left;
    struct node* right;
    int ht;
} node;

int height(node* current) {
    return current == nullptr ? -1 : current->ht;
}

int balanceFactor(node* root) {
    int leftHeight = height(root->left);
    int rightHeight = height(root->right);

    return leftHeight - rightHeight;
}

int calcHeight(node* n) {
    int leftHeight = height(n->left);
    int rightHeight = height(n->right);

    return max(leftHeight, rightHeight) + 1;
}

node* insert(node * root,int val) {
    /**
    First, recusively insert the item into the tree
    */
    if (root == nullptr) {
        root = new node();
        root->val = val;
    } else if (root->val < val) {
        root->right = insert(root->right, val);
        //the height can increase only because of the right node
        root->ht = std::max(root->ht, root->right->ht + 1);
    } else {
        root->left = insert(root->left, val);
        //the height can increase only because of the left node
        root->ht = std::max(root->ht, root->left->ht + 1);
    }

    //after insertion on this depth is complete check if rebalancing is required

    // the right subtree must be rebalanced
    if (balanceFactor(root) == -2) {
        node* r = root->right;
        node* rl = r->left;
        // it's a right right case
        if (balanceFactor(r) == -1) {
            r->left = root;
            root->right = rl;
            root->ht = calcHeight(root);
            r->ht = calcHeight(r);
            //return new root
            return r;
        } else { // it's a right left case
            node* rlr = rl->right;
            node* rll = rl->left;
            rl->left = root;
            root->right = rll;
            rl->right = r;
            r->left = rlr;

            root->ht = calcHeight(root);
            r->ht = calcHeight(r);
            rl->ht = calcHeight(rl);
            return rl;
        }
    } else if (balanceFactor(root) == 2) {
        node* l = root->left;
        node* lr = l->right;
        // it's a left left case
        if (balanceFactor(l) == 1) {
            l->right = root;
            root->left = lr;
            root->ht = calcHeight(root);
            l->ht = calcHeight(l);

            //return new root
            return l;
        } else { // it's a left right case
            node* lrl = lr->left;
            node* lrr = lr->right;
            lr->right = root;
            lr->left = l;
            root->left = lrr;
            l->right = lrl;

            root->ht = calcHeight(root);
            l->ht = calcHeight(l);
            lr->ht = calcHeight(lr);

            return lr;
        }
    }

    return root;
}
于 2018-08-12T11:26:35.767 回答
0

我编码的方式是,当您在树中搜索要删除的元素时,临时将您遍历的子链接(左或右)更改为遍历节点堆栈中的链接(实际上是临时父指针) . 然后从这个堆栈中弹出每个节点,恢复子指针,并重新平衡。

对于 C++ 编码,请参阅https://github.com/wkaras/C-plus-plus-intrusive-container-templates/blob/master/avl_tree.h中的删除成员函数(当前位于第 882 行)。

对于 C 编码,请参阅http://wkaras.webs.com/gen_c/cavl_impl_h.txt中的宏调用 L__(remove) 生成名称的函数。

我认为拥有父指针对插入没有任何用处。

如果要删除由节点指针而不是唯一键标识的节点,那么我认为使用父指针可能会更快。

于 2016-10-24T13:46:58.523 回答