使用List实现AVL树

时间:2013-05-07 08:20:55

标签: c# data-structures avl-tree

所以我使用参数化列表实现了二进制搜索树,即

List<Node> tree = new List<>();

树很好用。节点本身对其父节点或子节点一无所知。这是因为我根据索引计算位置,例如

                         If i is the index of some None N then:
                                N's left child is in tree[i*2] 
                                N's right child is in tree[(i*2)+1]

这个二叉树工作正常。但是现在我想把AVL树的功能放到它上面。我暂时陷入困境,因为我不知道如何在List上进行旋转。轮换时,我如何移动新根的子节点?事实是他们不得不转移指数吗?另外在List上执行此操作会给我一个问题,即每次添加节点时显示树都需要循环遍历List。这不会发生在O(logn)中,不再破坏AVL树的整个点。

我在C#中这样做。我只是想知道如何使用List或任何基于数组的数据结构有效地制作这个AVL树,该数据结构是可索引的而不是链接列表。这很重要。非常感谢一些代码来说明。

3 个答案:

答案 0 :(得分:2)

在数组/列表中表示树的方式对于堆数据结构来说很常见,但它几乎不适用于任何其他类型的树。特别是,你不能(有效地)为AVL树做这件事,因为每次轮换都需要太多的复制。

答案 1 :(得分:0)

对于我们没有malloc可用的嵌入式应用程序,我需要这个。在我尝试之前没有做任何类型的数据结构算法实现,如果可以的话。在编写代码时,我意识到我必须移动很多东西。我搜索了一个补救措施,并得到了这篇文章。

感谢Chris的回复,我不会再花时间了。我会找到其他方法来实现我需要的东西。

答案 2 :(得分:0)

我相信我找到了答案,Trick是在列表中上下移动子树,以便在旋转时不会覆盖有效节点。

void shiftUp(int indx, int towards) {
    if (indx >= size || nodes[indx].key == NULL) {
        return;
    }
    nodes[towards] = nodes[indx];
    nodes[indx].key = NULL;
    shiftUp(lChild(indx), lChild(towards));
    shiftUp(rChild(indx), rChild(towards));
}

void shiftDown(int indx, int towards) {
    if (indx >= size || nodes[indx].key == NULL) {
        return;
    }

    // increase size so we can finish shifting down
    while (towards >= size) { // while in the case we don't make it big enough
        enlarge();
    }

    shiftDown(lChild(indx), lChild(towards));
    shiftDown(rChild(indx), rChild(towards));
    nodes[towards] = nodes[indx];
    nodes[indx].key = NULL;
}

正如您所看到的,这是通过递归地探索每个子树直到NULL(在此定义为-1)节点然后向上或向下逐个复制每个元素来完成的。

通过这个我们可以定义根据这个Wikipedia Tree_Rebalancing.gif

命名的4种类型的旋转
void rotateRight(int rootIndx) {
    int pivotIndx = lChild(rootIndx);

    // shift the roots right subtree down to the right
    shiftDown(rChild(rootIndx), rChild(rChild(rootIndx)));
    nodes[rChild(rootIndx)] = nodes[rootIndx]; // move root too

    // move the pivots right child to the roots right child's left child
    shiftDown(rChild(pivotIndx), lChild(rChild(rootIndx)));

    // move the pivot up to the root
    shiftUp(pivotIndx, rootIndx);

    // adjust balances of nodes in their new positions
    nodes[rootIndx].balance--; // old pivot
    nodes[rChild(rootIndx)].balance = (short)(-nodes[rootIndx].balance); // old root
}

void rotateLeft(int rootIndx) {
    int pivotIndx = rChild(rootIndx);

    // Shift the roots left subtree down to the left
    shiftDown(lChild(rootIndx), lChild(lChild(rootIndx)));
    nodes[lChild(rootIndx)] = nodes[rootIndx]; // move root too

    // move the pivots left child to the roots left child's right child
    shiftDown(lChild(pivotIndx), rChild(lChild(rootIndx)));

    // move the pivot up to the root
    shiftUp(pivotIndx, rootIndx);

    // adjust balances of nodes in their new positions
    nodes[rootIndx].balance++; // old pivot
    nodes[lChild(rootIndx)].balance = (short)(-nodes[rootIndx].balance); // old root
}


// Where rootIndx is the highest point in the rotating tree
// not the root of the first Left rotation
void rotateLeftRight(int rootIndx) {
    int newRootIndx = rChild(lChild(rootIndx));

    // shift the root's right subtree down to the right
    shiftDown(rChild(rootIndx), rChild(rChild(rootIndx)));
    nodes[rChild(rootIndx)] = nodes[rootIndx];

    // move the new roots right child to the roots right child's left child
    shiftUp(rChild(newRootIndx), lChild(rChild(rootIndx)));

    // move the new root node into the root node
    nodes[rootIndx] = nodes[newRootIndx];
    nodes[newRootIndx].key = NULL;

    // shift up to where the new root was, it's left child
    shiftUp(lChild(newRootIndx), newRootIndx);

    // adjust balances of nodes in their new positions
    if (nodes[rootIndx].balance == -1) { // new root
        nodes[rChild(rootIndx)].balance = 0; // old root
        nodes[lChild(rootIndx)].balance = 1; // left from old root
    } else if (nodes[rootIndx].balance == 0) {
        nodes[rChild(rootIndx)].balance = 0;
        nodes[lChild(rootIndx)].balance = 0;
    } else {
        nodes[rChild(rootIndx)].balance = -1;
        nodes[lChild(rootIndx)].balance = 0;
    }

    nodes[rootIndx].balance = 0;
}

// Where rootIndx is the highest point in the rotating tree
// not the root of the first Left rotation
void rotateRightLeft(int rootIndx) {
    int newRootIndx = lChild(rChild(rootIndx));

    // shift the root's left subtree down to the left
    shiftDown(lChild(rootIndx), lChild(lChild(rootIndx)));
    nodes[lChild(rootIndx)] = nodes[rootIndx];

    // move the new roots left child to the roots left child's right child
    shiftUp(lChild(newRootIndx), rChild(lChild(rootIndx)));

    // move the new root node into the root node
    nodes[rootIndx] = nodes[newRootIndx];
    nodes[newRootIndx].key = NULL;

    // shift up to where the new root was it's right child
    shiftUp(rChild(newRootIndx), newRootIndx);

    // adjust balances of nodes in their new positions
    if (nodes[rootIndx].balance == 1) { // new root
        nodes[lChild(rootIndx)].balance = 0; // old root
        nodes[rChild(rootIndx)].balance = -1; // right from old root
    } else if (nodes[rootIndx].balance == 0) {
        nodes[lChild(rootIndx)].balance = 0;
        nodes[rChild(rootIndx)].balance = 0;
    } else {
        nodes[lChild(rootIndx)].balance = 1;
        nodes[rChild(rootIndx)].balance = 0;
    }

    nodes[rootIndx].balance = 0;
}

请注意,如果移位会覆盖节点,我们只需复制单个节点

至于效率存储每个节点中的平衡是必须的,因为在每个节点上获得高度差异将是非常昂贵的

int getHeight(int indx) {
    if (indx >= size || nodes[indx].key == NULL) {
        return 0;
    } else {
        return max(getHeight(lChild(indx)) + 1, getHeight(rChild(indx)) + 1);
    }
}

虽然这样做需要我们在修改列表时更新受影响节点的余额,但这可以通过仅更新严格必要的案例来有效地进行。 删除此调整是

// requires non null node index and a balance factor baised off whitch child of it's parent it is or 0
private void deleteNode(int i, short balance) {
    int lChildIndx = lChild(i);
    int rChildIndx = rChild(i);

    count--;
    if (nodes[lChildIndx].key == NULL) {
        if (nodes[rChildIndx].key == NULL) {

            // root or leaf
            nodes[i].key = NULL;
            if (i != 0) {
                deleteBalance(parent(i), balance);
            }
        } else {
            shiftUp(rChildIndx, i);
            deleteBalance(i, 0);
        }
    } else if (nodes[rChildIndx].key == NULL) {
        shiftUp(lChildIndx, i);
        deleteBalance(i, 0);
    } else {
        int successorIndx = rChildIndx;

        // replace node with smallest child in the right subtree
        if (nodes[lChild(successorIndx)].key == NULL) {
            nodes[successorIndx].balance = nodes[i].balance;
            shiftUp(successorIndx, i);
            deleteBalance(successorIndx, 1);
        } else {
            int tempLeft;
            while ((tempLeft = lChild(successorIndx)) != NULL) {
                successorIndx = tempLeft;
            }
            nodes[successorIndx].balance = nodes[i].balance;
            nodes[i] = nodes[successorIndx];
            shiftUp(rChild(successorIndx), successorIndx);
            deleteBalance(parent(successorIndx), -1);
        }
    }
}

类似于插入,这是

void insertBalance(int pviotIndx, short balance) {
    while (pviotIndx != NULL) {
        balance = (nodes[pviotIndx].balance += balance);

        if (balance == 0) {
            return;
        } else if (balance == 2) {
            if (nodes[lChild(pviotIndx)].balance == 1) {
                rotateRight(pviotIndx);
            } else {
                rotateLeftRight(pviotIndx);
            }
            return;
        } else if (balance == -2) {
            if (nodes[rChild(pviotIndx)].balance == -1) {
                rotateLeft(pviotIndx);
            } else {
                rotateRightLeft(pviotIndx);
            }
            return;
        }

        int p = parent(pviotIndx);

        if (p != NULL) {
            balance = lChild(p) == pviotIndx ? (short)1 : (short)-1;
        }

        pviotIndx = p;
    }
}

正如你所看到的那样,只使用普通的“节点”数组,因为我从给定gitHub array-avl-tree的c代码中翻译了它,并从(我将在评论中发布的链接)进行了优化和平衡,但是它会工作得很好类似于列表

最后,我对AVL树或最佳实现知之甚少,所以我并不认为这是无bug或最有效但至少已经通过我的初步测试