首先说的应该是我们的搜索二叉树是的,而这个有序是中序的有序
-
相对其他两个操作搜索二叉树的删除应该是最复杂的这个复杂的删除还是应该画图的,如果不画图是很难说清楚也很难理清楚的
好了我们的图先派在这里了,下面操作也都是用这个图作为标准了
当我们只是查看搜索二叉树时候我们并不需要修改搜索二叉树里面的节点也就是元素的顺序没有被我们打乱,所以了我们只需要清楚一个比较重要的概念就是头节点这个也是我们判断一个结点位置很重要的条件我们的插入,还要删除主要的核心点也就是和父节点的比较当中 -
先来定义一颗搜索二叉树吧
#include <iostream>
using namespace std;
template<class T>
struct BSTNode
{
BSTNode(const T& data)
: _pLeft(nullptr)
, _pRight(nullptr)
, _data(data)
{}
BSTNode<T>* _pLeft;
BSTNode<T>* _pRight;
T _data;
};
这里也没有什么好说的c++里面初始化一下
- 插入
template<class T>
class BSTree
{
typedef BSTNode<T> Node;
typedef Node* PNode;
public:
BSTree()
: _pRoot(nullptr)
{}
bool Insert(const T& data)
{
if (nullptr == _pRoot)
{
_pRoot = new Node(data);
return true;
}
// 找带插入元素在二叉搜索树中的位置
PNode pCur = _pRoot;
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类型的,如果存在就直接结束掉
- 查找
PNode Find(const T& data)
{
PNode pCur = _pRoot;
while (pCur)
{
if (data == pCur->_data)
return pCur;
else if (data < pCur->_data)
pCur = pCur->_pLeft;
else
pCur = pCur->_pRight;
}
return nullptr;
}
插入里面判断存在就已经是查找了,这里查找还是比较简单的。
- 删除
bool Delete(const T& data)
{
PNode pCur = _pRoot;
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;
}
}
if (nullptr == pCur)
return false;
// 删除节点
/*
1. 待删除节点没有孩子
2. 待删除节点只有左孩子
3. 待删除节点只有右孩子
4. 待删除节点左右孩子均存在
*/
PNode pDelNode = pCur;
if (nullptr == pCur->_pLeft)
{
// 当前节点只有右孩子
if (pCur == _pRoot)
{
_pRoot = pCur->_pRight;
}
else
{
// 待删除节点为其双亲的左孩子
if (pCur == pParent->_pLeft)
pParent->_pLeft = pCur->_pRight;
else // 待删除节点为其双亲的右孩子孩子
pParent->_pRight = pCur->_pRight;
}
}
else if (nullptr == pCur->_pRight)
{
// 当前节点只有左孩子
if (pCur == _pRoot)
_pRoot = pCur->_pLeft;
else
{
if (pCur == pParent->_pLeft)
pParent->_pLeft = pCur->_pLeft;
else
pParent->_pRight = pCur->_pLeft;
}
}
else
{
// 当前节点左右孩子均存在
// 直接删除,不好删除,在右子树中找一个替代节点
pParent = pCur;
pCur = pCur->_pRight;
// 找中序遍历下的第一个节点
while (pCur->_pLeft)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
// 用替代节点中内容替换待删除节点
pDelNode->_data = pCur->_data;
pDelNode = pCur;
// 删除替代节点
if (pCur == pParent->_pLeft)
pParent->_pLeft = pCur->_pRight;
else
pParent->_pRight = pCur->_pRight;
}
delete pDelNode;
return true;
}
删除还是复杂好多,由于没有要求控制时间复杂度,其实搜素树的删除明显还是相对avl树简单很多吧但是就算这样其实还是挺麻烦的,因为多数情况下,拿脑壳想搜索二叉树的删除还是很麻烦是呀,我们要画图,画图。图我就在放这里了
-
我们需要先看被删除节点左孩子为空情况
左孩子为空时候,被删节点就没有左边的干扰了。右孩子又给左孩子大,我们的右孩子就直接可以先放到左孩子的位置,然后删除,那样整个搜索二叉树的顺寻结构其实也并没有被破坏。
-
被删除节点右孩子为空
被删节点后面的节点是直接可以上移的,主要考虑一下被删节点在根节点的左边还是在根节点的右边画画图思路就自然里清楚了。 -
被删节点左右孩子均存在的情况
当被删节点的左右孩子均存在时候,我们可以去查找被删节点的右孩子然后拿被删节点右孩子的中序遍历查找出来被删节点右孩子节点子树当中最小的节点,**这个节点其实是距离被删节点的值最接近的值**用这个值去替代被删节点。然后链接好以后删除被删节点就可以了。 -
完整代码
#include <iostream>
using namespace std;
template<class T>
struct BSTNode
{
BSTNode(const T& data)
: _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()
: _pRoot(nullptr)
{}
bool Insert(const T& data)
{
if (nullptr == _pRoot)
{
_pRoot = new Node(data);
return true;
}
// 找带插入元素在二叉搜索树中的位置
PNode pCur = _pRoot;
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;
}
PNode Find(const T& data)
{
PNode pCur = _pRoot;
while (pCur)
{
if (data == pCur->_data)
return pCur;
else if (data < pCur->_data)
pCur = pCur->_pLeft;
else
pCur = pCur->_pRight;
}
return nullptr;
}
bool Delete(const T& data)
{
PNode pCur = _pRoot;
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;
}
}
if (nullptr == pCur)
return false;
// 删除节点
/*
1. 待删除节点没有孩子
2. 待删除节点只有左孩子
3. 待删除节点只有右孩子
4. 待删除节点左右孩子均存在
*/
PNode pDelNode = pCur;
if (nullptr == pCur->_pLeft)
{
// 当前节点只有右孩子
if (pCur == _pRoot)
{
_pRoot = pCur->_pRight;
}
else
{
// 待删除节点为其双亲的左孩子
if (pCur == pParent->_pLeft)
pParent->_pLeft = pCur->_pRight;
else // 待删除节点为其双亲的右孩子孩子
pParent->_pRight = pCur->_pRight;
}
}
else if (nullptr == pCur->_pRight)
{
// 当前节点只有左孩子
if (pCur == _pRoot)
_pRoot = pCur->_pLeft;
else
{
if (pCur == pParent->_pLeft)
pParent->_pLeft = pCur->_pLeft;
else
pParent->_pRight = pCur->_pLeft;
}
}
else
{
// 当前节点左右孩子均存在
// 直接删除,不好删除,在右子树中找一个替代节点
pParent = pCur;
pCur = pCur->_pRight;
// 找中序遍历下的第一个节点
while (pCur->_pLeft)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
// 用替代节点中内容替换待删除节点
pDelNode->_data = pCur->_data;
pDelNode = pCur;
// 删除替代节点
if (pCur == pParent->_pLeft)
pParent->_pLeft = pCur->_pRight;
else
pParent->_pRight = pCur->_pRight;
}
delete pDelNode;
return true;
}
void InOrder()
{
_InOrder(_pRoot);
}
void Destroy()
{
_Destroy(_pRoot);
}
private:
void _InOrder(PNode pRoot)
{
if (pRoot)
{
_InOrder(pRoot->_pLeft);
cout << pRoot->_data << " ";
_InOrder(pRoot->_pRight);
}
}
void _Destroy(PNode& pRoot)
{
if (pRoot)
{
_Destroy(pRoot->_pLeft);
_Destroy(pRoot->_pRight);
delete pRoot;
pRoot = nullptr;
}
}
private:
PNode _pRoot;
};
void TestBSTree()
{
int array[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
BSTree<int> t;
for (auto e : array)
{
t.Insert(e);
}
//t.Delete(5);
//t.Delete(8);
t.Delete(6);
t.InOrder();
t.Destroy();
}
int main()
{
TestBSTree();
return 0;
}
我们删除节点6,然后中旬遍历打印节点,验证一下结果,和搜索二叉树的操作是否有问题。
被删节点6成功删除,没有啥问题。