二叉查找树
2016-07-05 22:02
281 查看
复习一下二叉查找树, 采用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*> getNodes 4000 ByKey(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; };
总结: 删除节点的实现,要注意细节,三种遍历方式需要熟悉。