在上一篇關於二叉搜索樹的筆記中,我自己實現了幾個常用的有關BST的成員函數。其中刪除指定結點的函數十分複雜,還要考慮被刪結點的三種可能的情況。
其實,還有另外一種思路來實現刪除結點這一操作。
假設我們要刪除的結點是u,可以發現,如果被刪結點u沒有左子樹,或者沒有右子樹,或者左右子樹都沒有的話,實際編碼過程中,被釋放的結點d就是結點u本身。
但是如果結點u的左右子樹均存在,實際編碼過程中,結點u改變的只是自身的關鍵值u->key,沒有被真正釋放。真正被釋放的結點d是結點u的右子樹中值最小的結點
(當然也可以是左子樹中值最大的結點,完全取決與你想如何實現)。因此,我們把關注點從結點u是否存在左右子樹,轉移到真正被釋放的結點d到底是什麼。
這就是實現刪除某一結點函數的新思路:
一、找到實際需要刪除並釋放的結點d
如果被刪結點u沒有左子樹,或者沒有右子樹,或者左右子樹都沒有,d = u
如果被刪結點u左右子樹均存在,d = u的右子樹中值最小的結點
二、找到結點d 的父結點f 與 d 的子結點s
三、重置結點f 的子結點
f == NULL , 說明結點d爲根結點,根結點root = s(結點u左右子樹均存在時,d 的父結點f 一定存在,不會有這種情況)
否則將f 的子結點變爲 s
四、釋放結點d
如果結點u左右子樹均存在(等價於d != u), u->key = d->key
最後釋放結點d
參考代碼如下:
//刪除指定結點
void DeleteNode(tree_node &root, tree_node u)
{
tree_node d; //結點d存放實際需要釋放的結點
tree_node f = NULL; //結點f存放結點d的父結點
tree_node s; //結點s存放結點d的子結點
//確定結點d
if (u->left == NULL || u->right == NULL) d = u;
else {
d = u->right;
while (d->left != NULL) d = d->left;
}
//確定結點s
if (d->left != NULL) {
s = d->left;
} else {
s = d->right; //此時結點s也可能爲NULL
}
//確定結點f
tree_node x = root;
while (x != NULL && x != d) {
f = x;
if (d->key < x->key)
x = x->left;
else
x = x->right;
}
if (x == NULL) return; //要刪除的結點不存在
//刪除並釋放結點d
if (f == NULL) root = s; //可以證明,這種情況下,結點u不可能同時存在左右子樹
else if (d == f->left)
f->left = s;
else
f->right = s;
if (d != u)
u->key = d->key; //結點u左右子樹均存在
free(d);
return;
}