二叉搜索树
又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树
1.若它的左子树不为空,则左子树上所有结点的值都小于根结点的值;
2.若它的右子树不为空,则右子树上所有结点的值都大于根结点的值;
3.它的左右子树也称为二叉搜索树。
二搜索树的插入操作
在向二叉树中插入新元素时,必须先检测这个元素是否在树中已经存在,如果搜索成功。则说明该元素已经存在,则不进行插入,否则将新元素加入到搜索停止的地方
//如果插入的结点在树中有的话,那就直接返回false,不进行插入
bool Insert(const T& data)
{
//先判断树是否为空树,如果是空树直接插入
if (NULL == _pRoot)
{
_pRoot = new Node(data);
return true;
}
//找待插入结点的位置
else//不是空树
{
PNode pCur = _pRoot;
PNode pParent = NULL;
while (pCur)
{
if (data < pCur->_data)
{
pParent = pCur;//把pCur的位置标记下来,这样方便后面插入结点
pCur = pCur->_pLeft;
}
else if (data>pCur->_data)
{
pParent = pCur;
pCur = pCur->_pRight;
}
else
return false;
}
//插入结点
pCur = new Node(data);
if (data < pParent->_data)//如果此节点的数据比待插入数据小,就把它插到左边
pParent->_pLeft = pCur;
else
pParent->_pRight = pCur;//如果此节点的数据比待插入数据小,就把它插到右边
return true;
}
}
二叉搜索树的查找操作
如果此树不是空树的话:
如果根结点的值等于待查找结点的值的话,返回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 NULL;
//在查找节点时,不用判断是空树的情况,因为如果是空树的话就不会进while循环,程序就直接返回了
//如果是空树,,那就直接返回空指针
if (NULL == _pRoot)
return NULL;
else//不是空树
{
PNode pCur = _pRoot;
while (pCur)
{
if (data == pCur->_data)
return pCur;
else if (data < pCur->_data)
pCur = pCur->_pLeft;
else
pCur = pCur->_pRight;
}
return NULL;
}
}
二叉搜索树的删除操作
第一种情况:待删除结点是叶子结点
第二种情况:待删结点只有左子树
第三种情况:待删除结点只有右子树
第四种情况:待删除结点左右孩子都有
由这四种情况可见:第一种情况可以和第二种情况放在一起考虑或者第一种情况和第三种情况放在一起考虑
bool Delete(const T&data)
{
//如果是空树,直接返回false
if (NULL == _pRoot)
return false;
//删除结点
PNode pCur = _pRoot;
PNode pParent = NULL;
while (pCur)//找到待删除结点的双亲结点
{
if (data == pCur->_data)//根结点的只能等于待删除结点的值
break;
else if (data < pCur->_data)//根结点的值大于待删除结点的值(左子树)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
else//根结点的值小于待删除结点的值(右子树)
{
pParent = pCur;
pCur = pCur->_pRight;
}
}
//待删除结点只有左子树或者是待删除结点是叶子结点
if (NULL == pCur->_pRight)
{
if (pCur == _pRoot)//如果删除的是根结点
_pRoot = pCur->_pLeft;//因为此范围内的结点没有右子树,所以可以直接使根结点的左子树成为根结点,
else
{
if (pCur == pParent->_pLeft)//待删除结点是根结点的左子树
pParent->_pLeft = pCur->_pLeft;
else//待删除结点是根结点的右子树,
pParent->_pRight = pCur->_pLeft;
}
}
//待删除结点只有右子树或者是待删除结点是叶子结点
else if (NULL == pCur->_pLeft)
{
if (pCur == _pRoot)//如果删除的是根结点
_pRoot = pCur->_pRight;
else
{
if (pCur == pParent->_pRight)//待删除结点是根结点的右子树
pParent->_pRight = pCur->_pRight;
else//待删除结点是根结点的左子树
pParent->_pLeft = pCur->_pRight;
}
}
//待删除结点左右孩子都有
else
{
pParent = pCur;
PNode pDel = pCur->_pRight;
//寻找个根结点的右子树中的最小值
while (pDel->_pLeft)//根结点的右子树的最小值是没有左孩子的
{
pParent = pDel;
pDel = pDel->_pLeft;
}
pCur->_data = pDel->_data;
pCur = pDel;//一定要记得把要删除的结点赋给pCur,因为在程序的最后面,统一删除的是pCur
if (pDel == pParent->_pLeft)
pParent->_pLeft = pDel->_pRight;
else
pParent->_pRight = pDel->_pRight;
}
delete pCur;
pCur = NULL;
return true;
}
循环实现二叉搜索树的插入操作、查找操作、删除操作。
代码:
#include<iostream>
using namespace std;
template <class T>
struct BSTreeNode
{
BSTreeNode<T> * _pLeft;
BSTreeNode<T> * _pRight;
T _data;
BSTreeNode(const T& data)
:_pLeft(NULL)
, _pRight(NULL)
, _data(data)
{}
};
template <class T>
class BSTree
{
typedef BSTreeNode<T> Node;
typedef BSTreeNode<T>* PNode;
public:
BSTree()
:_pRoot(NULL)
{}
//如果插入的结点在树中有的话,那就直接返回false,不进行插入
bool Insert(const T& data)
{
//先判断树是否为空树,如果是空树直接插入
if (NULL == _pRoot)
{
_pRoot = new Node(data);
return true;
}
//找待插入结点的位置
else//不是空树
{
PNode pCur = _pRoot;
PNode pParent = NULL;
while (pCur)
{
if (data < pCur->_data)
{
pParent = pCur;//把pCur的位置标记下来,这样方便后面插入结点
pCur = pCur->_pLeft;
}
else if (data>pCur->_data)
{
pParent = pCur;
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 NULL;
//在查找节点时,不用判断是空树的情况,因为如果是空树的话就不会进while循环,程序就直接返回了
//如果是空树,,那就直接返回空指针
if (NULL == _pRoot)
return NULL;
else//不是空树
{
PNode pCur = _pRoot;
while (pCur)
{
if (data == pCur->_data)
return pCur;
else if (data < pCur->_data)
pCur = pCur->_pLeft;
else
pCur = pCur->_pRight;
}
return NULL;
}
}
bool Delete(const T&data)
{
//如果是空树,直接返回false
if (NULL == _pRoot)
return false;
//删除结点
PNode pCur = _pRoot;
PNode pParent = NULL;
while (pCur)//找到待删除结点的双亲结点
{
if (data == pCur->_data)//根结点的只能等于待删除结点的值
break;
else if (data < pCur->_data)//根结点的值大于待删除结点的值(左子树)
{
pParent = pCur;
pCur = pCur->_pLeft;
}
else//根结点的值小于待删除结点的值(右子树)
{
pParent = pCur;
pCur = pCur->_pRight;
}
}
//待删除结点只有左子树或者是待删除结点是叶子结点
if (NULL == pCur->_pRight)
{
if (pCur == _pRoot)//如果删除的是根结点
_pRoot = pCur->_pLeft;//因为此范围内的结点没有右子树,所以可以直接使根结点的左子树成为根结点,
else
{
if (pCur == pParent->_pLeft)//待删除结点是根结点的左子树
pParent->_pLeft = pCur->_pLeft;
else//待删除结点是根结点的右子树,
pParent->_pRight = pCur->_pLeft;
}
}
//待删除结点只有右子树或者是待删除结点是叶子结点
else if (NULL == pCur->_pLeft)
{
if (pCur == _pRoot)//如果删除的是根结点
_pRoot = pCur->_pRight;
else
{
if (pCur == pParent->_pRight)//待删除结点是根结点的右子树
pParent->_pRight = pCur->_pRight;
else//待删除结点是根结点的左子树
pParent->_pLeft = pCur->_pRight;
}
}
//待删除结点左右孩子都有
else
{
pParent = pCur;
PNode pDel = pCur->_pRight;
//寻找个根结点的右子树中的最小值
while (pDel->_pLeft)//根结点的右子树的最小值是没有左孩子的
{
pParent = pDel;
pDel = pDel->_pLeft;
}
pCur->_data = pDel->_data;
pCur = pDel;//一定要记得把要删除的结点赋给pCur,因为在程序的最后面,统一删除的是pCur
if (pDel == pParent->_pLeft)
pParent->_pLeft = pDel->_pRight;
else
pParent->_pRight = pDel->_pRight;
}
delete pCur;
pCur = NULL;
return true;
}
void InOrder()
{
cout << "InOrder:> ";
_InOrder(_pRoot);
}
private:
void _InOrder(PNode pRoot)
{
if (pRoot)
{
_InOrder(pRoot->_pLeft);
cout << pRoot->_data<<" ";
_InOrder(pRoot->_pRight);
}
}
private:
PNode _pRoot;
};
void Test()
{
int a[] = { 1, 4, 6, 7, 8, 9, 2, 5, 3, 0 };
BSTree<int> Tree;
int i = 0;
int sz = sizeof(a) / sizeof(a[0]);
for (i = 0; i < sz; ++i)
Tree.Insert(a[i]);//插入操作
Tree.InOrder();//使用中序遍历输出二叉搜索树,因为中序遍历的结果与二叉搜索树的结果相同
cout << endl;
查找节点时,如果找不到那就返回空指针,如果找到了那就返回此值的地址
cout << Tree.Find(10) << endl;//寻找操作
cout << Tree.Find(8) << endl;//寻找操作
cout << Tree.Find(5) << endl;//寻找操作
//Tree.Delete(0);//删除操作
//Tree.Delete(5);//删除操作
//Tree.Delete(4);//删除操作
//Tree.Delete(6);//删除操作
//Tree.Delete(8);//删除操作
//Tree.Delete(7);//删除操作
Tree.InOrder();
cout << endl;
}
int main()
{
Test();
system("pause");
return 0;
}
运行结果:
递归实现二叉搜索树的插入操作、查找操作、删除操作。
代码:
#include<iostream>
using namespace std;
#include<stdio.h>
template<class T>
struct BinarySearchTreeNode
{
BinarySearchTreeNode<T>* _pLeft;
BinarySearchTreeNode<T>* _pRight;
T _data;
BinarySearchTreeNode(const T&data)
: _pLeft(NULL)
, _pRight(NULL)
, _data(data)
{}
};
template<class T>
class BSTree
{
typedef BinarySearchTreeNode<T> Node;
typedef BinarySearchTreeNode<T>* PNode;
public:
BSTree()
:_pRoot(NULL)
{}
bool Insert(const T& data)
{
return _Insert(_pRoot, data);
}
void InOrder()
{
cout << "InOrder:> ";
_InOrder(_pRoot);
}
//查找某结点,如果查找成功,那就返回此节点的地址,如果不成功,那就返回空指针
PNode Find(const T&data)
{
return _Find(_pRoot, data);
}
bool Delete(const T&data)
{
return _Delete(_pRoot, data);
}
private:
bool _Delete(PNode &pRoot, const T&data)//因为需要修改根结点的指向,所以使用二级指针或者一级指针的引用
{
if (NULL == pRoot)
return false;
PNode pDel = pRoot;
/*不管是在左子树还是在右子树,需要删除结点的值都要与树中的结点进行比较,
所以把这些进行比较的语句统一在一起,可提高效率*/
if (data > pRoot->_data)
return _Delete(pRoot->_pRight, data);
else if (data < pRoot->_data)
return _Delete(pRoot->_pLeft, data);
else
{
if (NULL == pRoot->_pRight)//右子树为空或左右子树都为空的结点
{
pRoot = pRoot->_pLeft;//递归中只关心根,不用判断其他情况,直接删除就好
delete pDel;
}
else if (NULL == pRoot->_pLeft)//左子树为空或左右子树都为空的结点
{
pRoot = pRoot->_pRight;
delete pDel;
}
else//左右子树都不为空的结点
{
pDel = pRoot->_pRight;
while (pDel->_pLeft)//找到最小的结点
pDel = pDel->_pLeft;
pRoot->_data = pDel->_data;
return _Delete(pRoot->_pRight, pDel->_data);
}
}
return false;
}
private:
PNode _Find(PNode pRoot, const T&data)
{
//如果是空树,那就直接返回空指针
if (NULL == pRoot)
return NULL;
PNode pCur = pRoot;
while (pCur)
{
if (data == pCur->_data)
return pCur;
else if (data > pCur->_data)
return _Find(pCur->_pRight, data);
return _Find(pCur->_pLeft, data);
}
return NULL;
}
private:
void _InOrder(PNode pRoot)
{
if (pRoot)
{
_InOrder(pRoot->_pLeft);
cout << pRoot->_data << " ";
_InOrder(pRoot->_pRight);
}
}
private:
bool _Insert(PNode& pRoot,const T& data)//需要修改根结点的指向,所以需要传二级指针或者也可以传一级指针的引用
{
//如果是空树,那就直接插入
if (NULL == pRoot)//切记是判断,而不是赋值
{
pRoot = new Node(data);
return true;
}
else
{
if (data < pRoot->_data)
return _Insert(pRoot->_pLeft,data);
else
{
if (data > pRoot->_data)
return _Insert(pRoot->_pRight, data);
return false;
}
}
}
private:
PNode _pRoot;
};
void Test()
{
int a[] = { 1, 4, 6, 7, 8, 9, 2, 5, 3, 0 };
int i = 0;
int sz = sizeof(a) / sizeof(a[0]);
BSTree<int> Tree;
for (i = 0; i < sz; ++i)
Tree.Insert(a[i]);//插入操作
Tree.InOrder();//使用中序遍历输出插入的结果
cout << endl;
cout << Tree.Find(1) << endl;//查找操作
cout << Tree.Find(3) << endl;//查找操作
cout << Tree.Find(5) << endl;//查找操作
cout << Tree.Find(1) << endl;//查找操作
cout << Tree.Find(10) << endl;//查找操作
//Tree.Delete(1);//删除操作
//Tree.Delete(3);//删除操作
//Tree.Delete(5);//删除操作
//Tree.Delete(9);//删除操作
//Tree.Delete(7);//删除操作
//Tree.Delete(6);//删除操作
Tree.Delete(4);//删除操作
Tree.InOrder();
cout << endl;
}
int main()
{
Test();
system("pause");
return 0;
}
运行结果: