二叉查找树(Binary Search Tree),也称二叉搜索树、有序二叉树(ordered binary tree),排序二叉树(orted binary tree),是指一棵空树或者具有下列性质的二叉树:
- 若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
- 若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
- 任意节点的左、右子树也分别为二叉查找树;
- 没有键值相等的节点
二叉查找树相比于其他数据结构的优势在于查找、插入的时间复杂度较低,均为O(log n)。二叉查找树是基础性数据结构,用于构建更为抽象的数据结构,如集合、multiset、关联数组等。
如图就是一颗BST树
BST树的实现:
template<typename T>
class BSTree
{
public:
BSTree():_root(nullptr){}
// 非递归实现BST树的插入操作
void noninsert(const T &val);
// 非递归实现BST树的删除操作
void nonremove(const T &val);
// 非递归实现层序遍历(从根节点开始,一层一层按从左向右的顺序打印BST树节点的值)
void nonlevelOrder();
//查看val的值是否存在
bool query(const T &val);
private:
// 定义BST树节点的类型
struct BSTNode
{
BSTNode(T data=T()):_data(data),_left(nullptr),_right(nullptr)
{}
T _data;
BSTNode *_left;
BSTNode *_right;
};
};
BST树的插入:如果bst是空树,则将s所指结点作为根节点插入,若val等于b的根节点的数据域之值,则返回,若val小于b的根节点的数据域之值,则把s所指节点插入到左子树中,把s所指节点插入到右子树中(新插入节点总是叶子节点)。
template<class T>
void BSTree<T>::noninsert(const T &val)
{
if(_root == nullptr)
{
_root = new BSTNode(val);
return;
}
BSTNode *pre = nullptr;
BSTNode *cur = _root;
while(cur != nullptr)
{
pre = cur;
if(val < cur->_data)
{
cur = cur->_left;
}
else if(val > cur->_data)
{
cur = cur->_right;
}
else
{
return;
}
}
if(val < pre->_data)
{
pre->_left = new BSTNode(val);
}
else
{
pre->_right = new BSTNode(val);
}
}
BST树的删除:在删除BST树的节点时,会发现当前的节点会有三种情况
- 被删除的节点下面没有子节点。
- 被删除的节点下面只有一个子节点。
- 被删除的节点下面有两个子节点。
如果要删除67的值我们可以用前驱节点代替当前待删除的节点,然后直接删除前驱节点即可。
前驱节点:当前节点左子树中最大的值。 后继节点:当前节点右子树中最小的值。
template<class T>
void BSTree<T>::nonremove(const T &val)
{
// 1. 从_root开始寻找值为val的节点,cur指向它
BSTNode *pre = nullptr;
BSTNode *cur = _root;
while(cur != nullptr)
{
if(val < cur->_data)
{
pre = cur;
cur = cur->_left;
}
else if(val > cur->_data)
{
pre = cur;
cur = cur->_right;
}
else
{
break;
}
}
if(cur == nullptr)
return;
// 2. 先判断是否满足情况3,如果满足,需要找cur的前驱节点,用前驱把cur节点的值给覆盖掉,直接删前驱
if(cur->_left != nullptr && cur->_right != nullptr)
{
BSTNode *old = cur;
pre = cur;
cur = cur->_left;
while(cur->_right != nullptr)
{
pre = cur;
cur = cur->_right;
}
old->_data = cur->_data;
}
// 3. 删除情况1和情况2 直接删除cur指针指向的节点就可以了
BSTNode *child = cur->_left;
if(child == nullptr)
{
child = cur->_right;
}
if(pre == nullptr) // cur指向的根节点
{
_root = child;
}
else
{
// 要把删除节点的孩子赋给cur父节点相应的地址域里面
if(cur == pre->_left)
{
pre->_left = child;
}
else
{
pre->_right = child;
}
}
delete cur;
}
BST树的遍历
// 非递归实现层序遍历(从根节点开始,一层一层按从左向右的顺序打印BST树节点的值)
template<class T>
void BSTree<T>::nonlevelOrder()
{
// 1.如果_root为空,直接返回
if(_root == nullptr)
return;
// 2._root -> queue
queue<BSTNode*> que;
que.push(_root);
// 3.循环判断队列是否为空, 不为空取出队头元素,分别判断左右孩子是否为nullptr,不为空
// 就要依次入队,然后打印队头元素,继续判断下一个队头元素
while(!que.empty())
{
BSTNode *front = que.front();
cout<<front->_data<<" ";
que.pop();
if(front->_left != nullptr)
{
que.push(front->_left);
}
if(front->_right != nullptr)
{
que.push(front->_right);
}
}
}
val查找:
template<class T>
bool BSTree<T>::query(const T &val)
{
if(_root == nullptr)
{
cout<<"empty tree"<<endl;
return true;
}
BSTNode *cur = _root;
while(cur != nullptr)
{
if(val < cur->_data)
{
cur = cur->_left;
}
else if(val > cur->_data)
{
cur = cur->_right;
}
else
{
cout<<"exist"<<endl;
return true;
}
}
cout<<"not exist"<<endl;
return false;
}
测试用例:
int main(int argc, char* argv[])
{
BSTree<int> bst;
bst.noninsert(80);
bst.noninsert(67);
bst.noninsert(100);
bst.noninsert(75);
bst.noninsert(50);
bst.noninsert(120);
bst.nonlevelOrder();
cout<<endl;
bst.query(75);
bst.nonremove(75);
bst.nonlevelOrder();
cout<<endl;
bst.query(75);
cout<<endl;
return 0;
}