目录
一、搜索二叉树概念
搜索二叉树是一种特殊的二叉树,从根节点开始,左子树根节点的值一定当前节点值小,右子树根节点的值一定比当前节点大。
根据搜索二叉树的特点:
对搜索二叉树中序遍历,就会得到一个递增的序列。
可以类似二分查找,从根节点开始,查找值比当前节点值大去右子树找,比当前节点值小去左子树找,若值相等则查找完毕,一直到空节点则查找失败。
二、搜索二叉树实现
1.完整代码
#pragma once
template<class T>
struct BSTNode
{
BSTNode(const T& data = T())
: _pLeft(nullptr), _pRight(nullptr), _data(data)
{}
BSTNode<T>* _pLeft;
BSTNode<T>* _pRight;
T _data;
};
template<class T>
class BSTree
{
typedef BSTNode<T> Node;
typedef Node* PNode;
public:
BSTree() : _root(nullptr)
{}
// 根据二叉搜索树的性质查找:找到值为data的节点在二叉搜索树中的位置
PNode Find(const T& data)
{
PNode pCur = _root;
while (pCur)
{
if (data > pCur->_data)
pCur = pCur->_pRight;
else if (data < pCur->_data)
pCur = pCur->_pLeft;
else
return pCur;
}
return nullptr;
}
bool Insert(const T& data)
{
// 如果树为空,直接插入
if (nullptr == _root)
{
_root = new Node(data);
return true;
}
// 按照二叉搜索树的性质查找data在树中的插入位置
PNode pCur = _root;
// 记录pCur的双亲,因为新元素最终插入在pCur双亲左右孩子的位置
PNode pParent = nullptr;
while (pCur)
{
pParent = pCur;
if (data < pCur->_data)
pCur = pCur->_pLeft;
else if (data > pCur->_data)
pCur = pCur->_pRight; // 元素已经在树中存在
else
return false;
}
// 插入元素
pCur = new Node(data);
if (data < pParent->_data)
pParent->_pLeft = pCur;
else
pParent->_pRight = pCur;
return true;
}
bool Erase(const T& data)
{
// 如果树为空,删除失败
if (nullptr == _root)
return false;
// 查找在data在树中的位置
PNode pCur = _root;
PNode pParent = nullptr;
while (pCur)
{
if (data == pCur->_data)
break;
else if (data < pCur->_data)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
else
{
pParent = pCur;
pCur = pCur->_pRight;
}
}
// data不在二叉搜索树中,无法删除
if (nullptr == pCur)
return false;
if (nullptr == pCur->_pRight) // 当前节点只有左孩子或者左孩子为空---可直接删除
{
if (pParent == nullptr)
{
_root = pCur->_pLeft;
delete pCur;
}
else if (pParent->_pLeft == pCur)
{
pParent->_pLeft = pCur->_pLeft;
delete pCur;
}
else
{
pParent->_pRight = pCur->_pLeft;
delete pCur;
}
}
else if (nullptr == pCur->_pLeft) // 当前节点只有右孩子---可直接删除
{
if (pParent == nullptr)
{
_root = pCur->_pRight;
delete pCur;
}
else if (pParent->_pLeft == pCur)
{
pParent->_pLeft = pCur->_pRight;
delete pCur;
}
else
{
pParent->_pRight = pCur->_pRight;
delete pCur;
}
}
else // 当前节点左右孩子都存在
{
PNode temp = pCur;
pParent = pCur;
pCur = pCur->_pRight;
while (pCur->_pLeft)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
temp->_data = pCur->_data;
if (pParent->_pLeft == pCur)
pParent->_pLeft = pCur->_pRight;
else
pParent->_pRight = pCur->_pRight;
delete pCur;
}
return true;
}
//递归
bool InsertR(const T& key)
{
return _InsertR(_root, key);
}
PNode FindR(const T& key)
{
return _FindR(_root, key);
}
bool EraseR(const T& key)
{
return _EraseR(_root, key);
}
void InOrder()
{
_InOrder(_root);
std::cout << std::endl;
}
private:
bool _InsertR(PNode& _root, T key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
if (key < _root->_data)
return _InsertR(_root->_pLeft,key);
else if (key > _root->_data)
return _InsertR(_root->_pRight,key);
else
return false;
}
bool _EraseR(PNode& _root, T key)
{
if (nullptr == _root)
return false;
if (key < _root->_data)
return _EraseR(_root->_pLeft,key);
else if (key > _root->_data)
return _EraseR(_root->_pRight,key);
else
{
PNode temp = _root;
if (_root->_pLeft == nullptr)
_root = _root->_pRight;
else if(_root->_pRight == nullptr)
_root = _root->_pLeft;
else //删除有两个孩子节点
{
PNode min = _root->_pRight;
while (min->_pLeft)
min = min->_pLeft;
std::swap(min->_data, _root->_data);
return _EraseR(_root->_pRight, key);
}
delete temp;
return true;
}
}
Node* _FindR(PNode _root, T key)
{
if (nullptr == _root)
{
return nullptr;
}
if (_root->_data < key)
_FindR(_root->_pRight, key);
else if (_root->_data > key)
_FindR(_root->_pLeft, key);
else
return _root;
}
void _InOrder(PNode _root)
{
if (_root == nullptr)
return;
_InOrder(_root->_pLeft);
std::cout << _root->_data << ' ';
_InOrder(_root->_pRight);
}
private:
PNode _root;
};
2.删除
删除是所有操作种的难点,先找到要删除的节点。接下删除该节点需要分三种情况来解决
1.要删除节点左子树为空。直接将记录的父节点连接到要删除节点的右子树。
2.右子树为空 。与上一种情况相反。
3.左右子树都不为空。有两种做法,第一种可以将当前节点左子树的最右节点与当前节点的值进行交换(在非递归中可以将左子树最右节点的值赋值给当前节点的值),释放其左子树最右节点。第二种也完全类似,只不过使用的交换释放的节点从当前节点的左子树最右节点换成当前节点的右子树最左节点,其目的都是在找最靠近当前节点值的两个值,由这两个值来取代当前节点位置就不会破坏搜索二叉树的结构。
2.1非递归
bool Erase(const T& data)
{
// 如果树为空,删除失败
if (nullptr == _root)
return false;
// 查找在data在树中的位置
PNode pCur = _root;
PNode pParent = nullptr;
while (pCur)
{
if (data == pCur->_data)
break;
else if (data < pCur->_data)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
else
{
pParent = pCur;
pCur = pCur->_pRight;
}
}
// data不在二叉搜索树中,无法删除
if (nullptr == pCur)
return false;
if (nullptr == pCur->_pRight) // 当前节点只有左孩子或者左孩子为空---可直接删除
{
if (pParent == nullptr)
{
_root = pCur->_pLeft;
delete pCur;
}
else if (pParent->_pLeft == pCur)
{
pParent->_pLeft = pCur->_pLeft;
delete pCur;
}
else
{
pParent->_pRight = pCur->_pLeft;
delete pCur;
}
}
else if (nullptr == pCur->_pLeft) // 当前节点只有右孩子---可直接删除
{
if (pParent == nullptr)
{
_root = pCur->_pRight;
delete pCur;
}
else if (pParent->_pLeft == pCur)
{
pParent->_pLeft = pCur->_pRight;
delete pCur;
}
else
{
pParent->_pRight = pCur->_pRight;
delete pCur;
}
}
else // 当前节点左右孩子都存在
{
PNode temp = pCur;
pParent = pCur;
pCur = pCur->_pRight;
while (pCur->_pLeft)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
temp->_data = pCur->_data;
if (pParent->_pLeft == pCur)
pParent->_pLeft = pCur->_pRight;
else
pParent->_pRight = pCur->_pRight;
delete pCur;
}
return true;
}
2.2递归
PNode& _root,这里使用引用,很好地记录了上一个节点的指针,使得能够链接插入的节点。
bool EraseR(const T& key)
{
return _EraseR(_root, key);
}
bool _EraseR(PNode& _root, T key)
{
if (nullptr == _root)
return false;
if (key < _root->_data)
return _EraseR(_root->_pLeft,key);
else if (key > _root->_data)
return _EraseR(_root->_pRight,key);
else
{
PNode temp = _root;
if (_root->_pLeft == nullptr)
_root = _root->_pRight;
else if(_root->_pRight == nullptr)
_root = _root->_pLeft;
else //删除有两个孩子节点
{
PNode min = _root->_pRight;
while (min->_pLeft)
min = min->_pLeft;
std::swap(min->_data, _root->_data);
return _EraseR(_root->_pRight, key);
}
delete temp;
return true;
}
}
3.插入
比较简单,依据先找到要插入值的位置,再进行插入的思路。
3.1非递归
bool Insert(const T& data)
{
// 如果树为空,直接插入
if (nullptr == _root)
{
_root = new Node(data);
return true;
}
// 按照二叉搜索树的性质查找data在树中的插入位置
PNode pCur = _root;
// 记录pCur的双亲,因为新元素最终插入在pCur双亲左右孩子的位置
PNode pParent = nullptr;
while (pCur)
{
pParent = pCur;
if (data < pCur->_data)
pCur = pCur->_pLeft;
else if (data > pCur->_data)
pCur = pCur->_pRight; // 元素已经在树中存在
else
return false;
}
// 插入元素
pCur = new Node(data);
if (data < pParent->_data)
pParent->_pLeft = pCur;
else
pParent->_pRight = pCur;
return true;
}
3.2递归
同删除一样,参数_root使用了引用,引用父节点的左或右指针。
bool InsertR(const T& key)
{
return _InsertR(_root, key);
}
bool _InsertR(PNode& _root, T key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
if (key < _root->_data)
return _InsertR(_root->_pLeft,key);
else if (key > _root->_data)
return _InsertR(_root->_pRight,key);
else
return false;
}
4.查找
4.1非递归
PNode Find(const T& data)
{
PNode pCur = _root;
while (pCur)
{
if (data > pCur->_data)
pCur = pCur->_pRight;
else if (data < pCur->_data)
pCur = pCur->_pLeft;
else
return pCur;
}
return nullptr;
}
4.2递归
PNode FindR(const T& key)
{
return _FindR(_root, key);
}
Node* _FindR(PNode _root, T key)
{
if (nullptr == _root)
{
return nullptr;
}
if (_root->_data < key)
_FindR(_root->_pRight, key);
else if (_root->_data > key)
_FindR(_root->_pLeft, key);
else
return _root;
}