C++实现基于二叉查找树的有序符号表(下)——学习笔记
二. 二叉查找树的实现
/ (5) deleteMin / delete
deleteMin方法作用主要是删除二叉树当中的拥有最小键的节点,下面从简单的情况开始理解。
还是这样一个简单的二叉查找树,如何删除最小键的节点呢,当然是不断地深入根节点的左子树,直到找到最小的键,在此图当中,根节点只有一个左子节点,这个左子节点的左子节点指针为空,也就意味着,没有比它更小的键了,所以我们要删除的目标就是它,与此同时也可以发现它同时也没有右子节点,所以只需要直接删除这个节点即可,不需要考虑处理右子节点的问题。
如果键为3 的右子节点存在,如图(a)所示,此时键为3的节点仍然是最小节点,但在删除的时候还需要处理右子节点,保证右边节点不丢失,毕竟我们只删除最小的键节点,删除过程如下所示:
if(bstnode->LeftNode == NULL) //意味着没有比他更小的键
{
BSTNode<KEY, VALUE> *bstnode_r;
bstnode_r = bstnode->RightNode;
delete bstnode;
return bstnode_r;
}
这里我暂时的处理办法是将右子节点赋值给一个新的同类型节点,以便返回,然后删除最小键节点,再返回这个新的节点,无论这个新节点指针被赋值了空指针(最小键节点并没有右节点),还是原来最小键节点的右子节点指针(不为空),它都作为返回值 赋值给它的父节点作为左子节点,仍然保持二叉查找树的有序性。
如果将问题规模扩大,其原理也相同,只不过可能需要多往下深入几层左子树而已。deleteMin方法可以使用递归来完成,代码较简单,基线条件就是判断左子节点是否为空。
template<typename KEY, typename VALUE> void BST<KEY, VALUE>::deleteMin()
{
m_root = deleteMin(m_root);
}
template<typename KEY, typename VALUE> void BST<KEY, VALUE>::deleteMin(BSTNode<KEY, VALUE> *bstnode)
{
if(bstnode->LeftNode == NULL)
{
BSTNode<KEY, VALUE> *bstnode_r;
bstnode_r = bstnode->RightNode;
delete bstnode;
return bstnode_r;
}
bstnode-LeftNode = deleteMin(bstnode->LeftNode);
bstnode->N = size(bstnode->RightNode) + size(bstnode->LeftNode) + 1;
return bstnode;
}
相比与deleteMin方法,delete方法较复杂,设想这样的情况,某一个节点被删除,如果这个节点的两个子节点都不为空,那么如何处理这两个子节点,使整个二叉查找树仍然保持有序性呢。有一种办法是删除了某个指定节点后,让这个节点的右子树中的最小键的节点A作为被删除节点B的替代节点,将被删除节点B的左子节点指针赋值给A的左子节点指针,右边也一样。这样就可以保持整个二叉树的有序性,如下图所示。
在这个例子中,假设我们需要删除键为4的节点。首先找到这个指定节点,然后找到它的右子树中最小的键(使用min(bstnode->RightNode)
),从图中不难看出,4键节点的右子树中最小的键是5,因此我们需要将5替换到4的位置,并妥善处理5的两个子节点保持有序性。如何处理呢,做法同deleteMin方法类似,不过在找到4键右子树的最小键后,不需要删除其对应节点,我们要做的是让deleteMin(4键节点的右子节点)返回如图(d)所示的节点指针,虽然还是返回了7键节点的指针,但此时以7键节点为根节点的二叉查找树已经发生改变,这个根节点的左子节点指针不再指向5这个最小键节点,而时指向了最小键节点的右子节点(键为6),有序性保持不变,deleteMin()完成了这个操作。
接着就可以让键为5(指定键节点右子树中最小键的节点)的节点代替键为4(指定键节点)的节点了,操作很简单,赋值左右节点指针即可,代码如下:
template<typename KEY, typename VALUE> BSTNode* BST<KEY, VALUE>::deleteMIN_tool(BSTNode<KEY, VALUE> *bstnode)
{
if(bstnode->LeftNode == NULL) return bstnode->RightNode; //不删除 最小节点
bstnode->LeftNode = deleteMIN_tool(bstnode->LeftNode);
bstnode->N = size(bstnode->LeftNode) + size(bstnode->RightNode) + 1;
return bstnode;
}
template<typename KEY, typename VALUE> void BST<KEY, VALUE>::delete(KEY key)
{
m_root = delete(m_root, key);
}
template<typename KEY, typename VALUE> BSTNode<KEY, VALUE>* BST<KEY, VALUE>::delete(BSTNode<KEY, VALUE>* bstnode, KEY key)
{
if(bstnode == NULL) return NULL;
if(key < bstnode->key) bstnode->LeftNode = delete(bstnode->LeftNode);
else if(key > bstnode->key) bstnode->RightNode = delete(bstnode->RightNode);
else
{
if(bstnode->LeftNode == NULL)
{
BSTNode<KEY, VALUE> *new_node;
new_node = bstnode->RightNode;
delete bstnode;
return new_node;
}
if(bstnode->RightNode == NULL)
{
BSTNode<KEY, VALUE> *new_node;
new_node = bstnode->LeftNode;
delete bstnode;
return new_node;
}
BSTNode<KEY, VALUE> *deleted_bstnode;
deleted_bstnode = bstnode;
bstnode = min(bstnode->RightNode);
deleted_bstnode->RightNode = deleteMin(deleted_bstnode->RightNode);
bstnode->LeftNode = deleted_bstnode->LeftNode;
bstnode->RightNode = deleted_bstnode->RightNode;
}
bstnode->N = size(bstnode->LeftNode) + size(bstnode->RightNode) + 1;
return bstnode;
}
/ (5) keys
keys方法的作用是指定某个键的范围,找出范围内所有的键。实现这个键之前,可以先看看将全部key按顺序打印输出的方法,通过递归方式可以简单地实现:
template<typename KEY, typename VALUE> void BST<KEY, VALUE>::print_all()
{
print_all(m_root);
}
template<typename KEY, typename VALUE> void BST<KEY, VALUE>::print_all(BSTNode<KEY, VALUE>* bstnode)
{
if(bstnode == NULL) return;
print_all(bstnode->LeftNode);
cout << bstnode->key << endl;
print_all(bstnode->RightNode);
}
以此为基础,实现查找范围内所有键就不那么困难了,只需要修改以上代码即可。思想是我们在遍历地过程中判断该键是否在范围内,如果在范围内,输出即可,或者将其写入一个队列当中,不在范围内就跳过。
template<typename KEY, typename VALUE> void BST<KEY, VALUE>::keys(KEY lok, KEY hik)
{
keys(m_root, lok, hik);
}
template<typename KEY, typename VALUE> void BST<KEY, VALUE>::keys(BSTNode<KEY, VALUE> *x, KEY lok, KEY hik)
{
if(x == NULL) return;
if(lok < x->key) keys(x->LeftNode, lok, hik);
if(lok <= x->key && hik >= x->key) cout << x->key;
if(lok > x->key) keys(x->RightNode, lok, hik);
}
二叉查找树的基本方法大概就是这些。下一篇可能会记录一下平衡树的笔记。