如何修复BST删除功能中的边缘情况?

时间:2009-07-26 01:06:46

标签: c++ binary-tree

假设我的删除尝试按顺序(从左到右)重新平衡树。

我正在编写一个BinarySearchTree类,我的删除功能目前正常工作(我相信 - 我希望< 3)在大多数情况下。我有几个边缘案件可以抗衡:

  • 删除根节点将不起作用,因为它没有父节点。
  • 在下一个有两个孩子的最后一个案例中,如果它的最右边的后代是正确的孩子本身,那么newCurr将指向next,这将导致问题,因为newCurr(又名下一个)将最终与New交换。 / LI>

提出的根删除解决方案:

  1. 我的解决方案,即删除根并使用我的BST类的成员函数将setRoot设置为New(使该节点成为根),然后将New曾经设置为0 / NULL。

    答:这有效,但我必须把案件工作放在一起。

  2. 在BST类中有一个虚拟父节点,它只是将根节点作为它的右手对象(由billyswong建议)。

    答:这可能有用,但我觉得我应该有特殊的案例来处理它。

  3. 删除两个孩子的建议解决方案:

    1. 我的解决方案是暂时存储New的位置,将New的位置设置在New的右子项中,然后删除临时指针。

      答:这样可行,但不像我想的那样优雅。

    2. “旋转节点”(不知道如何做到这一点,由Agor建议)。
    3. 这是我的代码:

      if (root()->value() == val)
      {
          delete root();
          this->setRoot(New);
          newCurr->setLeft(0);
      }    
      else if (newCurr == next)
      {
          Node *temp = New;
          newCurr->setRight(New->right());
          delete temp;
      } 
      

      海报请告诉我这段代码1)是否有效2)是最佳的。

      编辑:抱歉我在功能结束时对camelcaps的使用不一致。我想不出对我的变量名更好的描述,但new是C ++中定义的关键字。

      Edit2:发布了重构代码,但错误仍然存​​在。

      void BinarySearchTree::del(int val)
      {
      //curr refers to the parent of next
      //next is the node we will want to delete
          Node* curr = 0;
          Node* next = root(); 
      
      //these will only be used if you get into
      //the last case, where you have two children
      //on next
          Node* newCurr = curr;
          Node* New = next;
      
      //boolean value to check if you found the value
      //in the binary search tree
          bool found;
      
      //set curr and next; will be false if val not found
          found = setCurrAndNext(val, curr, next);
      
      
      //get next to the node needing deletion
      //and set curr to its parent
      //pass by ref function  
      //return value is that you found it  
          if (found)
          {
              setNewCurrAndNew (newCurr, New);
          }   
          else
          { 
              return;    
          }
      
      /* next is a leaf */
      
          if (nextIsLeaf(next))
          {
              handleLeafCase(curr, next);
              return;
          }   
      /* next has a single child */        
          else if (nextHasSingleChild(next))       
          {
              if(leftIsChild(next))  
              {
                  handleLeftCase(curr, next);
              }
              else
              {
                  handleRightCase(curr, next);
              }
          }
      /* next has two children */    
          else
          {  
             if (newCurr == next)
             {
                  Node *temp = New;
                  newCurr->setRight(New->right());
                  delete temp;
             } 
             else if (next == curr->left())
             {
                  if (New == newCurr->left())
                  {
                     curr->setLeft(New);
                     newCurr->setLeft(next);
                  }
                  else
                  {
                     curr->setLeft(New);
                     newCurr->setRight(next);
                  }
             }
             else
             {
                  if (New == newCurr->left())
                  {
                     curr->setRight(New);
                     newCurr->setLeft(next);
                  }    
                  else
                  {
                     curr->setRight(New);
                     newCurr->setRight(next);
                  }
             }
      
             if (next->left() == 0 && next->right() == 0)
             {
                  newCurr->setRight(0);
                  newCurr->setLeft(0);
      
                  delete next;
             }
             else
             {
                  if (next->left() == 0)
                  {
                     newCurr->setRight(next->left());
                  }
                  else
                  {
                     newCurr->setLeft(next->right());
                  }
      
                     delete next;
                  }
              }
          }
      }
      

2 个答案:

答案 0 :(得分:1)

  

删除根节点将不起作用,因为它没有父节点

我对这一点的想法是,给你的根一个虚拟的父节点,一个节点不包含实际有用的值,但根节点作为它的右子节点。然后,每个可删除节点都将拥有其父节点,并且root()将能够像其他节点一样更加统一地处理。那么你也不需要特殊的方法来记住树是否也是空的。

修改:found = setCurrAndNext(val, curr, next);如何设置currnext? AFAIK C / C ++总是按值传递。我在辅助函数中闻到了一些错误。

答案 1 :(得分:1)

我不能给你代码或任何东西,但你正在寻找的是一个“旋转”操作符。基本上,它处理“烦人”的删除情况 - 当你删除一个有两个孩子的树,这两个孩子都有两个孩子,比如根,还有树中的任何其他节点。

旋转的作用基本上是在所涉及的节点之间,在一个非常局部的区域中交换孩子(创伤,我知道),以便保持孩子的排序(左边较小,右边较大)。它类似于优先级队列的“冒泡”功能。

这是维基百科(http://en.wikipedia.org/wiki/Tree_rotation)页面。

希望这能让你走上正轨!

编辑以回应评论: 对不起,我应该解释一下。当我说“树”时,我的意思是节点,维基百科页面应该仍然有用。由于二进制搜索树的数学递归定义,您可以将每个节点视为自己的子树,因此我的初始语句(保持不变)。但这只是与你的问题相关,所以继续......:)