上篇處理了左子樹最大節點即為左孩子的簡單情況。本篇解決「左子樹最大節點位於左子樹深處」的一般情況,並引入遞歸輔助函數統一處理普通節點刪除。
此時需找到左子樹中的最大節點(前驅節點),將其數據複製到根節點,然後刪除該前驅節點(它至多只有左子樹)。
pLeftMax。pLeftMax->data 複製到當前根節點。pLeftMax(因為它是最大值,必無右子樹,直接讓其父節點的右指標指向其左子樹即可)。pLeftMax。else {
TREE_NODE* pLeftMax = find_max_node(pTreeNode->left_child);
if (pLeftMax == pTreeNode->left_child) {
// 上篇已處理的情況
*ppTreeNode = pTreeNode->left_child;
(*ppTreeNode)->right_child = pTreeNode->right_child;
(*ppTreeNode)->right_child->parent = *ppTreeNode;
(*ppTreeNode)->parent = NULL;
} else {
// 一般情況:前驅節點在左子樹深處
pTreeNode->data = pLeftMax->data; // 複製數據
pLeftMax->parent->right_child = pLeftMax->left_child; // 繞過 pLeftMax
if (pLeftMax->left_child)
pLeftMax->left_child->parent = pLeftMax->parent;
pTreeNode = pLeftMax; // 待釋放節點改為 pLeftMax
}
}
free(pTreeNode);
return TRUE;
static void test7() {
TREE_NODE* pTreeNode = NULL;
assert(TRUE == insert_node_into_tree(&pTreeNode, 10));
assert(TRUE == insert_node_into_tree(&pTreeNode, 6));
assert(TRUE == insert_node_into_tree(&pTreeNode, 8)); // 6 的右孩子,為左子樹最大值
assert(TRUE == insert_node_into_tree(&pTreeNode, 15));
assert(TRUE == delete_node_from_tree(&pTreeNode, 10));
assert(8 == pTreeNode->data); // 新根數據為 8
assert(NULL == pTreeNode->parent);
assert(NULL == pTreeNode->left_child->right_child); // 原 8 節點已被移除
// 釋放記憶體...
}
當目標節點非根節點時,邏輯與根節點刪除高度相似,但需處理父節點的指標更新。為避免代碼重複,提取 _delete_node_from_tree 專門處理非根節點。
STATUS delete_node_from_tree(TREE_NODE** ppTreeNode, int data) {
TREE_NODE* pTreeNode;
TREE_NODE* pLeftMax;
if (NULL == ppTreeNode || NULL == *ppTreeNode)
return FALSE;
pTreeNode = find_data_in_tree_node(*ppTreeNode, data);
if (NULL == pTreeNode)
return FALSE;
if (*ppTreeNode == pTreeNode) {
// 根節點刪除邏輯(同上,省略)
// ...
free(pTreeNode);
return TRUE;
}
// 非根節點:委派給輔助函數
return _delete_node_from_tree(pTreeNode);
}
_delete_node_from_tree 設計要點完整代碼將在後續文章展示,核心思想是「找前驅覆蓋數據,再刪除前驅節點」,將雙子刪除轉化為單子/無子刪除。
掌握這些模式,即可實現高可靠度的 BST 刪除操作。