C++实现基于二叉查找树的有序符号表(下)——学习笔记

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);
}

二叉查找树的基本方法大概就是这些。下一篇可能会记录一下平衡树的笔记。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值