复习一下二叉查找树, 采用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;
};
总结: 删除节点的实现,要注意细节,三种遍历方式需要熟悉。