一. 二叉搜索树的特点
- 若左子树不为空,左子树的值小于等于根节点
- 若右子树不为空,右子树的值大于等于根节点
- 他的左右子树也都是二叉搜索树
- 二叉搜索树可以支持插入相同的值,也可以支持插入不同的值,map/set/multimap/multiset系列容器底层就是二叉搜索树,multi系列支持插入相同的值(本章节我实现的二叉树代码不支持插入相同值)
二. 二叉树的性能
- 最优:如果是个完全二叉树,高度是 logN
- 最差:单支树,高度是 N
注意 这里和二分查找对比一下,二分查找也可以实现 O(logN) 的查找,但是二分查找有两个比较大的缺陷
- 需要有序,并且存储在可以下表访问的结构中
- 插入删除需要挪动数据,效率低
三. 二叉树的实现
(1) 二叉树的实现的基本构成
由链表实现,节点内保存左右结点的地址
template <class K>
struct BSTnode
{
K _key;
BSTnode<K>* _left;
BSTnode<K>* _right;
BSTNode(const K& key)
:_key(key)
,_left(nullptr)
,_right(nullptr)
{}
};
template<class K>
struct BSTtree
{
public:
typedef BSTnode<K> Node;
private:
Node* _root = nullptr;
};
(2) 二叉树的插入
- 树为空,直接新增节点,节点地址复制给root指针
- 树不空,插⼊值比当前结点⼤往右走,插⼊值比当前结点小往左走,找到空位置,插入新节点
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else //节点存在就不再插入
return false;
}
cur = new Node(key);
//判断在父节点的右还是左
if (parent->_key < key)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
return true;
}
(3) 二叉树的查找
- 从根节点查找,比根的值大就往右边走,比根的值小就往左边走
- 最多查找高度次,如果找到空还没有,就是树里面没有这个值
这里跟插入逻辑一样
bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
cur = cur->_right;
}
else if (cur->_key > key)
{
cur = cur->_left;
}
else
{
return true;
}
}
return false;
}
(4) 二叉树的删除(最重要,重点看)
- 首先要查找要删除的值存不存在,如果不存在就返回false。
- 如果存在,删除节点不能破坏二叉树特点,就分为两种情况
- 要删除的节点N左右子树都为空或有一方为空,把N的非空节点(或空节点)给N的父节点,
- 要删除的N节点左右都不为空,选N节点右子树的最小值(最左节点)或者左子树的最大节点(最右节点),和N节点交换值,然后删除被交换的节点,(被交换的节点一定符合第一种情况可以直接删)
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else
{
if (cur->_left == nullptr)
{
//这里要考虑删除节点为根节点的情况
if (parent == nullptr)
{
_root = cur->_right;
}
else
{
if (parent->_left == cur)
parent->_left = cur->_right;
else
parent->_right = cur->_right;
}
delete cur;
return true;
}
//这里是cur右节点为空的情况,就不实现了,逻辑和左节点为空的一样
else if (cur->_right == nullptr)
;
//左右节点都不为空
else
{
//寻找右树最小节点(最左节点)
Node* rightMinP = cur;
Node* rightMin = cur->_right;
while (rightMin->_left)
{
rightMinP = rightMin;
rightMin = rightMin->_left;
}
//把右数的最小值赋给要删除的节点,然后删除右数的最小节点
cur->_key = rightMin->_key;
if (rightMinP->_left == rightMin)
rightMinP->_left = rightMin->_right;
else
rightMinP->_right = rightMin->_right;
delete rightMin;
return true;
}
}
}
return false;
}