我正在尝试为二进制搜索树类编写一个方法来修改平衡的普通树,这使得树只在一侧有节点。
从元素出现在不平衡树中的顺序来看,在顺序遍历(左,中,右)之间似乎存在某种关系。
答案 0 :(得分:1)
迭代执行此操作的一种方法是使用树旋转。这个想法是这样的:
在伪代码中:
Node finalRoot = null;
while (currNode != null) {
if (currNode.left != null) {
currNode = rotateRight(currNode);
} else {
if (finalRoot == null) finalRoot = currNode;
currNode = currNode.right;
}
}
return finalRoot;
这种方法改编自Day-Stout-Warren算法,该算法是第一步。它在时间O(n)中运行。
答案 1 :(得分:1)
void unbalance(Node **pp) {
Node *p;
while ((p = *pp)) {
if (!p->left) {
pp = &p->right;
} else {
Node *tmp = p->left->right;
*pp = p->left;
p->left->right = p;
p->left = tmp;
}
}
}
...
Node *tree = ...;
unbalance(&tree);
此代码基于@ templatetypedef的答案。它使用一个指向指针的指针来避免孤立节点(它也摆脱了finalRoot
,它的唯一目的是避免孤立调用者传入的原始根。)
这个想法是沿着最右边的树枝走下树。如果从未有任何留下的孩子,我们只输入if
语句的第一部分并退出而不做任何事情。
当有左子树时,使用else
部分。在那种情况下,我们向右旋转树。
这具有从左子树上拉出一个节点的效果,即我们的左子树现在是一个较小的节点。我们重复此操作,直到左子树完全消失为止,此时我们再次进入if
的第一部分。然后我们转到剩下的右子树并重复整个事情。
在修改链表或树的结构时,指针指针通常很有用。它们让我们直接在原始指针上工作,而不是复制它们。在这种情况下,赋值*pp = ...
会将指针修改为其他位置(pp
指向的指针)。这可以是原始根(由调用者传入),也可以是树中的right
个指针之一(由pp = &p->right
设置)。需要进行此重新分配,因为else
分支中的树旋转会使节点周围的位置混乱,因此以前作为子树根的内容会进一步向下移动,而前左节点会被拉起以成为新的根节点。我们更新*pp
以保持指针更高的不变量(我们的调用者中的tree
变量或父节点的right
成员)指向的(新)根的子树。