在尝试从二进制树中删除节点时,如何修复相应的指针我该怎么办?我有一点麻烦。
我理解基本概念,我基本上只是修复指针...
所以,基本上,我的删除功能大部分已经完成,并且每个案例都已经有效(就我的大量测试而言,一切正常),我只错过了有2个孩子的案例节点。假设我们在处理该案例的else if
内:
我将有2个指针,currPtr
保存要删除的节点,prevPtr
保存父节点。我还有一个名为fromLeft
的变量,它定义currPtr
父是否来自prevPtr
上的左或右指针。然后,我调用另一个名为extractMin(Tree *t)
的函数,该函数提取树中的最低值。如果我以currPtr->right
为参数调用此函数,则currPtr
的后继将从树中提取(函数将从树中删除它,修复指针并返回节点指针)。后继指针将保存在tempPtr
。
这是我树的结构:
typedef int TreeElement;
typedef struct sTree {
TreeElement item;
struct sTree *left;
struct sTree *right;
} Tree;
提取功能的代码:
Tree *extractMin(Tree *tree) {
Tree *prevPtr = NULL;
Tree *currPtr = tree;
while(currPtr->left) {
prevPtr = currPtr;
currPtr = currPtr->left;
}
if(prevPtr) prevPtr->left = currPtr->right;
// inorder successor
return currPtr;
}
上面的代码缺少这里的情况,树只有一个节点,根节点,它将无法正常工作,它也不会检查树是否有任何节点,但是,当树有效时有一些节点。
那么,如何在else if
上修复节点删除的必要指针?另外,请记住,树节点上的left
和right
指针始终指向某处或NULL
。
顺便说一句,我想迭代这样做。
答案 0 :(得分:3)
已更新:因此,您希望保留节点的顺序,方法是将节点替换为直接顺序的后继节点或前一节点。
我们假设下面的树是有序的。节点的顺序是:
H < D < I < B < J < E < K < A < F < M < C < N < G < O
您要删除包含两个子节点的节点(A)。你拉出节点的前序(K)或后继(F)子节点代替原始子节点。让我们选择继任者。这就是你如何找到它:你遍历C的左边孩子,直到找到一个没有左孩子的孩子;这是A的直接继承者。
A
B C
D E F G
H I J K M N O
因此F被拉起,并且未触及A的左子树。但是,现在M也应该被拉起来,成为C的左边孩子(这是在你的extractMin()
中完成的。)
在所有重新安排后,你得到
F
B C
D E M G
H I J K N O
在代码中,这是我的解决方案。我向extractMin()
添加了NULL检查以简化调用者代码,因此我不需要else if
。
已更新 extractMin()
以涵盖tree
没有子女时的情况。
Tree *extractMin(Tree *parent, Tree *tree) {
if (!tree) return NULL;
Tree *prevPtr = NULL;
Tree *currPtr = tree;
while(currPtr->left) {
prevPtr = currPtr;
currPtr = currPtr->left;
}
if (prevPtr) {
prevPtr->left = currPtr->right;
} else if (parent) {
parent->right = currPtr->right;
}
// inorder successor
return currPtr;
}
// prevPtr is the parent, currPtr is the node to be deleted
Tree *successor = extractMin(currPtr, currPtr->right);
successor->left = currPtr->left;
successor->right = currPtr->right;
if (fromLeft) {
prevPtr->left = successor;
} else {
prevPtr->right = successor;
}
// Now currPtr can be destroyed
请注意,此代码未经过测试,因此我不保证: - )
请注意,像这样重复删除可能会使树不平衡(也就是说,某些路径会比其他路径长得多)。二进制排序树以这种方式进行删除,但也使用重新平衡后保持树接近理想值(每个叶子处于同一级别,如上面的第一个示例中所示)。
答案 1 :(得分:1)
教科书的答案是用它最左边的后代替换有问题的节点。
6
3 8
2 4 7 9
1 5 10
如果我们要删除3,我们可以将其替换为4,然后将5拉入4中的孔。我们总是可以这样做,它将保留有序遍历。
行。看着你的代码,这就是你想要的:
//in your function
else if (/*has 2 nodes*/) {
currPtr->item = extractMin(currPtr->right, &(currPtr->right))->item;
}
Tree *extractMin(Tree *tree, Tree ** back) {
Tree *currPtr = tree;
while(currPtr->left) {
back = &(currPtr->left);
currPtr = currPtr->left;
}
*back = currPtr->right;
// inorder successor
return currPtr;
}
**参数允许我们处理我们使用已删除节点的直接右子:
的情况 3 <--deleting this node
/ \ <--back points to this edge.
2 4 <--extractMin is called on this node and returns this node,
\
5 <-- (*back) now points to this node.
在2个案例中考虑新的ExtractMin。
back == &(prevPtr->left);
(例如修改* back将修改prevPtr-&gt; left)。所以它与上面的代码相同。back
指向我们无法以任何其他方式获得的链接(除非我们修改extractMin以更高一级开始)。 考虑它的另一种方法是(* back)总是指向currPtr(花点时间检查一下),所以如果我们要删除currPtr,则返回指向我们需要修改的边缘。
答案 2 :(得分:0)