目录
搜索二叉树的概念
搜索二叉树的操作
递归版本
二叉树的插入
二叉树的查找
二叉树的删除
非递归版本
二叉树的递归插入
二叉树的递归查找
二叉树的递归删除
在之前我们已经学习过了二叉树这一数据结构,本期我们将学习一种新的数据结构------搜索二叉树。
搜索二叉树的概念
我们把具有以下性质的树成为搜索二叉树:
- 若左子树不为空,则左子树所有节点的值都小于根节点的值。
- 若右子树不为空,则右子树所有节点的值都大于根节点的值。
- 所有子树均满足1和2两个性质。
搜索二叉树图示如下。
我们对搜索二叉树进行中序遍历,结果为0,1,2,3,4,5,6,7,8,9,同时我们规定搜索二叉树不能有相同的元素,所以搜索二叉树的功能就是排序和去重。
搜索二叉树的操作
递归版本
二叉树的插入
代码如下。
//2.插入
bool insert(K key)
{
//当树为空时
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
//当树不为空时
else
{
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
cur = new Node(key);
if (cur->_key < parent->_key)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
}
return true;
}
void InOrder()
{
_InOrder(_root);
}
void _InOrder(Node* root)
{
//如果为空树
if (root == nullptr)
return;
//不为空树
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
运行结果如下。
运行结果符合预期。
二叉树的查找
代码如下。
//3.查找
bool find(K key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key > key)
{
cur = cur->_left;
}
else if (cur->_key < key)
{
cur = cur->_right;
}
else
{
return true;
}
}
return false;
}
运行结果如下。
运行结果符合预期。
二叉树的删除
二叉树的各种操作中,最复杂的就是二叉树的删除,二叉树的删除总共分为两种情况。
第一种节点的删除只需要更改指针的指向即可。第二种较为复杂,我们需找到左子树的最大值或者右子树的最小值让其作为删除之后的搜索二叉树的根节点。
代码如下。
bool erase(K key)
{
//查找对应的元素
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else
{
//查找到了对应的元素,开始删除
//左孩子为空
if (cur->_left == nullptr)
{
if (parent == nullptr)
{
_root = cur->_right;
}
if (cur->_key < parent->_key)
{
parent->_left = cur->_right;
}
parent->_right = cur->_right;
delete cur;
}
//右孩子为空
else if (cur->_right == nullptr)
{
if (parent == nullptr)
{
_root = cur->_left;
}
if (cur->_key < parent->_key)
{
parent->_left = cur->_left;
}
parent->_right = cur->_left;
delete cur;
}
//左右孩子均不为空,找左子树最右的节点或者右子树最左的节点
else
{
Node* MinParent = cur;
Node* min = cur->_right;
while (min->_left)
{
MinParent = min;
min = min->_left;
}
//找到了最小的节点
cur->_key = min->_key;
if (MinParent->_left = min)
{
MinParent->_left = min->_right;
}
MinParent->_right = min->_right;
delete min;
}
return true;
}
}
return false;
}
运行结果如下。
运行结果符合预期。
非递归版本
二叉树的递归插入
代码如下。
//递归插入
void insertR(K key)
{
_insertR(_root, key);
}
void _insertR(Node*& root, K key)
{
if (root == nullptr)
{
root = new Node(key);
return;
}
if (root->_key > key)
{
_insertR(root->_left, key);
}
else if (root->_key < key)
{
_insertR(root->_right, key);
}
else
{
return;
}
}
二叉树的递归查找
Node* findR(K key)
{
return _findR(_root,key);
}
Node* _findR(Node* root, K key)
{
if (root == nullptr)
{
return nullptr;
}
if (root->_key < key)
{
_findR(root->_right,key);
}
else if (root->_key > key)
{
_findR(root->_left,key);
}
else
{
return root;
}
}
二叉树的递归删除
bool eraseR(K key)
{
return _eraseR(_root, key);
}
bool _eraseR(Node* &root,K key)
{
if (root == nullptr)
{
return false;
}
if (root->_key < key)
{
_eraseR(root->_right, key);
}
else if (root->_key > key)
{
_eraseR(root->_left, key);
}
else
{
Node* erase = root;
if (root->_left == nullptr)
{
root = root->_right;
}
else if (root->_right == nullptr)
{
root = root->_left;
}
else
{
Node* min = root->_right;
while (min->_left)
{
min = min->_left;
}
swap(root->_key, min->_key);
//递归到右子树删除
_eraseR(root->_right, key);
}
delete erase;
return true;
}
递归中使用了引用,去接收根节点,这一点省去了插入和删除元素之后,节点之间的链接,大家可以画递归展开图进一步了解。
搜索二叉树优化
观察图1和图2。
图1和图2都是搜索二叉树,如果我们都去查找9这个元素,不难发现两个搜索二叉树都要去查找4次,但是明显图1的节点比图2的节点多了一倍,这也是影响搜索二叉树效率的一个关键原因------树上的节点分布不均衡,同样是N个节点,如果在理想情况下即满二叉树,查找一个元素的复杂度为O(logN),但是如果分布不均匀,就像一个链表一样,查找一个节点的时间复杂度为O(N),所以在后续我们会学习平衡二叉树如AVL树和红黑树来避免像图2的情景发生。
以上便是搜索二叉树的所有内容。
本期内容到此结束^_^