二叉搜索树也称有序搜索树,它具有如下的性质:
1.每个结点都有一个作为搜索依据的关键码(key),每个结点的关键码都不一样
2.左子树上所有结点的关键码都小于当前根节点的关键码
3.右子树上所有结点的关键码都大于当前根节点的关键吗
4.左右子树都是二叉搜索树
二叉搜索树的操作:插入,删除,查找,遍历
一、插入---Insert
当进行插入后,不能破坏二叉搜索树的结构,因此就要查找插入的结点的正确位置。此时就需要比较其key与当前根节点的大小,如果key大于当前根节点的key,就应该往右子树走并继续查找;否则,就应该在左子树查找;当该key与树中某一节点的key相等时,插入失败。
//非递归
bool Insert(const K& key) //插入一个数
{
//空树
if (_root == NULL)
{
_root = new Node(key);
return true;
}
Node* cur = _root;
Node* parent = _root;
//Node* newNode = new Node(key);
while (cur)
{
if (cur->_key > key) //在左树
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key) //在右树
{
parent = cur;
cur = cur->_right;
}
else //已经存在
{
return false;
}
}
//不存在
if (parent->_key > key) //找到正确位置,判断插入左子树还是右子树
{
parent->_left = new Node(key);
}
else
{
parent->_right = new Node(key);
}
}
二、删除---Remove
同Insert一样,首先要找到key存在的位置,如果遍历完整棵树都没有找到就返回;假如已经找到,就要从三个方面考虑:
1.删除的是只有一个孩子结点的结点---假如有右孩子(right),就用右孩子代替该位置;否则就用左孩子(left)代替
2.删除的是叶子结点---由于删除了叶子结点后,它的parent也是指向NULL,所以这种情况归为情况1的特殊情况,只不过是它的左/右孩子为NULL
3.删除的是有两个孩子结点的结点---有两种方法:找左子树的最大节点(最右结点)来替换,或者找右子树的最小结点(最左结点)替换。
bool Remove(const K& key)
{
//空树
if (_root == NULL)
return false;
Node* cur = _root;
Node* parent = NULL;
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else
{
//找到key所在的结点----删除
if (cur->_left == NULL)
{
if (parent == NULL)
{
_root = _root->_right;
}
else
{
//判断cur是parent的左孩子还是右孩子
if (parent->_left == cur)
parent->_left = cur->_right;
else if(parent->_right == cur)
parent->_right = cur->_right;
}
}
else if (cur->_right == NULL)
{
if (parent == NULL)
{
_root = _root->_left;
}
else
{
if(parent->_left == cur)
parent->_left = cur->_left;
else if(parent->_right == cur)
parent->_right = cur->_left;
}
}
else //左右节点都不为空
{
parent = cur;
//找cur右树的最左(小)结点,并利用替换法删除
Node* minRight = cur->_right;
while (minRight->_left)
{
parent = minRight;
minRight = minRight->_left;
}
cur->_key = minRight->_key;
if(parent->_left == minRight)
parent->_left = minRight->_right;
else
parent->_right = minRight->_right;
delete minRight;
}
return true;
}
}
return false;
}
三、查找---Find
查找也是要找与key值对应的结点。查找的方法与Insert查找的方法一样,找到返回true,否则返回false
bool Find(const K& key) //查找一个数
{
//空树
if (_root == NULL)
return false;
Node* cur = _root;
while(cur)
{
if (cur->_key > key) //查找的数在左树
{
cur = cur->_left;
}
else if(cur->_key < key) //查找的数在右树
{
cur = cur->_right;
}
else //找到key
{
return true;
}
}
if (cur == NULL) //没找到
{
return false;
}
}
四、遍历---InOrder
根据二叉搜索树的性质,采用中序遍历的结果是递增的
void _InOrder(Node* root)
{
if (root == NULL)
return ;
Node* cur = root;
if (cur)
{
_InOrder(cur->_left);
cout<<cur->_key<<" ";
_InOrder(cur->_right);
}
}
下面是前三种操作的递归方法:
bool _InsertRec(Node*& root,const K& key)
{
//空树
if (root == NULL)
{
root = new Node(key);
return true;
}
Node* cur = root;
if (cur->_key > key)
return _InsertRec(cur->_left,key);
else if (cur->_key < key)
return _InsertRec(cur->_right,key);
else
return false; //已经存在key的结点
}
bool _RemoveRec(Node*& root,const K& key)
{
//空树
if (root == NULL)
return false;
if (root->_key > key)
return _RemoveRec(root->_left,key);
else if (root->_key < key)
return _RemoveRec(root->_right,key);
//找到要删除的结点
else
{
Node* parent = root;
Node* cur = root;
if (root->_left == NULL)
root = root->_right;
else if(root->_right == NULL)
root = root->_left;
else
{
parent = root;
root = root->_right;
while (root->_left)
{
root = root->_left;
}
if (parent->_left == root)
parent->_left = root;
else
parent->_right = root;
}
return true;
}
}
bool _FindRec(Node*& root,const K& key)
{
if (root == NULL)
{
return false;
}
if (root->_key > key)
{
_FindRec(root->_left,key);
}
else if (root->_key < key)
{
_FindRec(root->_right,key);
}
else
{
return true;
}
}