二叉查找树

复习一下二叉查找树, 采用C++来编写。

将节点结构将其作为二叉查找树特用的,定义在类的内部里。

(贴代码的时候我是按照顺序贴的)

template<typename Key, typename Value>
class BSTree
{
public:
  typedef struct _TagNode
  {
    Key _Key;
    Value _Value;
    struct _TagNode* _pLeft,* _pRight;    //子树
    size_t _n;                //以该节点为根的子树节点总数(包含自己)
    _TagNode(const Key& keyData, const Value& valueData, size_t nNumber)
      : _Key(keyData), _Value(valueData), _n(nNumber), _pLeft(nullptr), _pRight(nullptr)
    {

    }
    _TagNode()
      : _Key(Key()), _Value(Value()), _n(0), _pLeft(nullptr), _pRight(nullptr)
    {

    }
  }Node;

该类提供如下方法:

public:
  BSTree() : _pRoot(nullptr)
  {

  }

  ~BSTree()
  {
    if(_pRoot)
        Destory(_pRoot);    //销毁树
  }  

  //获取根节点
  const Node* getRoot() 
  {
    return _pRoot;
  }

  //获取一个节点
  Node* get(const Key& key)
  {
    return get(_pRoot, key);
  }

    //添加一个新的节点
  void put(const Key& key, const Value& value)
  {
    put(&_pRoot, key, value);
  }

  //树的最小值节点
  Node* min()
  {
    if (_pRoot == nullptr)
      return nullptr;
    return min(_pRoot);
  }

  //树的最大值节点  
  Node* max()
  {
    if (_pRoot == nullptr)
      return nullptr;
    return max(_pRoot);
  }

  //找到相同的键值获取(向下取)
  Node* floor(const Key& key)
  {
    return floor(_pRoot, key);
  }

  //向上取
  Node* ceiling(const Key& key)
  {
    return ceiling(_pRoot, key);
  }

  void deleteMin()
  {
    deleteMin(&_pRoot);
  }

  void deleteNodeByKey(const Key& key)
  {
    deleteNodeByKey(&_pRoot, key);
  }

  //某个区间内的所有节点
  vector<Node*> getNodesByKey(const Key& keyLo, const Key& keyHi)
  {
    vector<Node*> vet;
    getNodesByKey(_pRoot, vet, keyLo, keyHi);
    return vet;
  }




- 实现的代码都是私有的并且都是静态函数还将用到二级指针

private:  //实现
    static void Destory(Node* pNode)
    {
       if(pNode == nullptr){
           return;
       }
       Destory(pNode->_pLeft);
       Destory(pNode->_pRight);
       delete pNode;
    }

  static void getNodesByKey(Node* pNode, vector<Node*>& vet, const Key& keyLo, const Key& keyHi)
  {
    if (pNode == nullptr)
      return;
    int nLo = keyLo - pNode->_Key, nHi = keyHi - pNode->_Key;
    if (nLo <= 0 && nHi >= 0) {
      vet.push_back(pNode); 
    }
    if (nLo < 0) getNodesByKey(pNode->_pLeft, vet, keyLo, keyHi); //说明键值还可以更小,需要继续往小的找
    if(nHi > 0) getNodesByKey(pNode->_pRight, vet, keyLo, keyHi);//说明键值还可以更大
  }

  static void deleteMin(Node** ppNode)
  {
    vector<Node*> arrEachNode;        //之前遍历的节点 用于更新节点值
    while (*ppNode) {
      if ((*ppNode)->_pLeft == nullptr) { //没有左节点 说明该节点为最小
        Node* pTmp = (*ppNode)->_pRight;
        delete (*ppNode);                 //删除节点
        *ppNode = pTmp;
        //在这里遍历方向并不是重点
        for (auto it = arrEachNode.rbegin(); it != arrEachNode.rend(); ++it) {
          --((*it)->_n);
        }
        break;
      }
      arrEachNode.push_back(*ppNode);     //走过的节点
      ppNode = &((*ppNode)->_pLeft);
    }
  }

  //弹出一个最小的node
  static Node* popMinNode(Node** ppNode)
  {
    vector<Node*> arrEachNode;        //之前遍历的节点 用于更新节点值
    while (*ppNode) {
      if ((*ppNode)->_pLeft == nullptr) { //没有左节点 说明该节点为最小
        Node* pTmp = *ppNode;             //保存弹出的节点
        *ppNode = (*ppNode)->_pRight;
        //在这里遍历方向并不是重点
        for (auto it = arrEachNode.rbegin(); it != arrEachNode.rend(); ++it) {
          --((*it)->_n);
        }
        pTmp->_pLeft = pTmp->_pRight = nullptr;
        pTmp->_n = 1;
        return pTmp;
      }
      arrEachNode.push_back(*ppNode);     //走过的节点
      ppNode = &((*ppNode)->_pLeft);
    }
    return nullptr;
  }
  /*
  //删除一个节点,需要遍历走过的节点,重新计算值
  static void deleteNode(Node** ppNode, vector<Node*>& arr)
  {

  }
  */
  static void deleteNodeByKey(Node** ppNode, const Key& key)
  {
    vector<Node*> arrEachNode;        //之前遍历的节点 用于更新节点值
    while (1) {
      if (*ppNode == nullptr)         //找不到就走人
        return;
      arrEachNode.push_back(*ppNode); //记录
      int nNum = key - (*ppNode)->_Key;
      if (nNum < 0) ppNode = &((*ppNode)->_pLeft);
      else if (nNum > 0) ppNode = &((*ppNode)->_pRight);
      else {  //找到了
        Node* pTmp = nullptr;
        //之前走过的路都要减1
        for (auto it = arrEachNode.rbegin(); it != arrEachNode.rend(); ++it) {
          --((*it)->_n);
        }
        int nSwitch = 0;    //用于判断是否有孩子节点为空 如果有为空 直接将另外的孩子赋值到该节点上即可
        if ((*ppNode)->_pLeft == nullptr) nSwitch = 1;
        else if((*ppNode)->_pRight == nullptr) nSwitch = 2;
        if (nSwitch != 0) {
          if (nSwitch == 1) pTmp = (*ppNode)->_pRight;  //没有左孩子 可以直接将右孩子赋予节点
          else if (nSwitch == 2) pTmp = (*ppNode)->_pLeft;
          //else assert(0 && "nSwitch");
          delete (*ppNode);                 //删除节点
          //在这里并不需要重新计算该节点的值,因为孩子节点的值已经确定了下来,另外的孩子节点是null 所以并不需要更新
          *ppNode = pTmp;
          return;
        }
        pTmp = popMinNode(&((*ppNode)->_pRight));
        pTmp->_pRight = (*ppNode)->_pRight;
        pTmp->_pLeft = (*ppNode)->_pLeft;
        delete (*ppNode);
        pTmp->_n = 1;     //重新计算节点的值,因为2个孩子的值并不确定所以需要更新。
        if (pTmp->_pLeft) pTmp->_n += pTmp->_pLeft->_n;
        if (pTmp->_pRight) pTmp->_n += pTmp->_pRight->_n;
        *ppNode = pTmp;
        return;
      }
    }
  }

  static Node* min(Node* pNode)
  {
    while (pNode->_pLeft) {
      pNode = pNode->_pLeft;
    }
    return pNode;
  }

  static Node* max(Node* pNode)
  {
    while (pNode->_pRight) {
      pNode = pNode->_pRight;
    }
    return pNode;
  }

  static Node* ceiling(Node* pNode, const Key& key)
  {
    while (pNode) {
      int nNum = key - pNode->_Key;
      if (nNum > 0) pNode = pNode->_pRight;    //给定的key大于当前根节点 只能往右找大值了。
      else if (nNum == 0) break;              //找到break
                                              //key小于当前根节点 说明 只能往左找 找接近于key的值 如果找不到说明就是这个值了(向上取)
      else {
        Node* pTmp = floor(pNode->_pLeft, key);
        if (pTmp)
          pNode = pTmp;
        break;
      }
    }
    return pNode;
  }

  static Node* floor(Node* pNode, const Key& key)
  {
    while (pNode) {
      int nNum = key - pNode->_Key;
      if (nNum < 0) pNode = pNode->_pLeft;    //给定的key小于当前根节点 只能往左找小值了。
      else if (nNum == 0) break;              //找到break
      //key大于当前根节点 说明 只能往右找 找接近于key的值 如果找不到说明就是这个值了(向下取)
      else {
        Node* pTmp = floor(pNode->_pRight, key);
        if (pTmp)
          pNode = pTmp;
        break;
      }
    }
    return pNode;
  }

  static void put(Node** ppNode, const Key& key, const Value& value)
  {
    vector<Node*> arrEachNode;        //之前遍历的节点 用于更新节点值
    while (1) {
      if (*ppNode == nullptr) {
        *ppNode = new Node(key, value, 1);
        //在这里遍历方向并不是重点
        for (auto it = arrEachNode.rbegin(); it != arrEachNode.rend(); ++it) {
          ++((*it)->_n);
        }
        return;
      }
      arrEachNode.push_back(*ppNode);
      int nNum = key - (*ppNode)->_Key;
      if (nNum < 0) ppNode = &((*ppNode)->_pLeft);
      else if (nNum > 0) ppNode = &((*ppNode)->_pRight);
      else {
        (*ppNode)->_Value = value;
        return;
      }
    }
  }

  static Node* get(Node* pNode, const Key& key)
  {
    while (pNode) {
      int nNum = key - pNode->_Key;
      if (nNum < 0) pNode = pNode->_pLeft;
      else if (nNum > 0) pNode = pNode->_pRight;
      else break;
    }
    return pNode;
  }

类的属性只有一个

private:
  Node* _pRoot;
 };

总结: 删除节点的实现,要注意细节,三种遍历方式需要熟悉。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值