二叉排序树概念
二叉搜索树又称二叉排序树(BSTree, Binary Search Tree),它或者是一颗空树,或者是具有以下性质的二叉树:
- 若它的左子树不为空,则左子树上所有结点的值都小于根结点的值
- 若它的右子树不为空,则右子树上所有结点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树
代码部分
BSTree框架
//创建二叉树结点
template<class K>
struct BSTNode
{
struct BSTNode<K>* _left;
struct BSTNode<K>* _right;
K _key;
//构造函数
BSTNode(const K& key)
:_key(key)
,_left(nullptr)
,_right(nullptr)
{}
};
template<class K>
class BSTree
{
typedef BSTNode<K> Node;
//.....
private:
Node* _root = nullptr;
};
查找操作
a、从根开始比较,查找,比根大往右走查找,比根小往左走。
b、最多查找高度次,走到空,还没找到,这个值就不存在。
非递归:
//查找
bool Find(const 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 EFind(const K& key)
{
return _EFind(_root, key);
}
bool _EFind(Node* root, const K& key)
{
if (root == nullptr)
return false;
if (root->_key > key)
{
return _EFind(root->_left, key);
}
else if (root->_key < key)
{
return _EFind(root->_right, key);
}
else
{
return true;
}
}
插入操作
a. 树为空,直接新增新节点,赋值给root指针
b. 树不空,按二叉搜索树性质查找插入位置,插入新节点
非递归:
//插入
bool Insert(const K& key)
{
//空
if (nullptr == _root)
{
_root = new Node(key);
return true;
}
//不空
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
//找到插入位置
if (parent->_key > key)
{
//左边插入
parent->_left = new Node(key);
}
else
{
//右边插入
parent->_right = new Node(key);
}
return true;
}
递归版本:利用指针的引用!!
bool EInsert(const K& key)
{
return _EInsert(_root, key);
}
//指针的引用!!!
bool _EInsert(Node*& root, const K& key)
{
if (root == nullptr)
{
root = new Node(key);
return true;
}
if (root->_key > key)
return _EInsert(root->_left, key);
else if (root->_key < key)
return _EInsert(root->_right, key);
else //相等
{
return false;
}
}
删除操作**
首先查找元素是否在二叉搜索树中,若不存在,则返回,否则要删除的结点可能分下面四种情况:
a. 要删除的结点无孩子结点
b.要删除的结点只有左孩子
c.要删除的结点只有右孩子
d.要删除的结点有左、右孩子结点
表面上有四种,但是实际上情况a可以与b、c情况合并,因此真正删除的情况如下:
-
情况b:删除该节点且使被删除结点的双亲结点指向被删除结点的左孩子结点
-
情况c:删除该结点,并且使被删除结点的双亲结点指向被删除结点的右孩子结点。
-
情况d:找到被删除结点中序遍历的前一个(或者后一个)结点,将他们俩的值交换,交换后的那个结点。就变成了b\c两种情况。
非递归版本:
//删除
bool Erase(const 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//找到了
{
//左右孩子都没有,就是叶子,其实可以和b、c两种情况合并
if (cur->_left == nullptr && cur->_right == nullptr)
{
if (cur == _root)
{
_root = nullptr;
}
else if (parent->_left == cur)
{
parent->_left = nullptr;
}
else if (parent->_right == cur)
{
parent->_right = nullptr;
}
delete cur;
}
else //不是叶子
{
//只有左为空
if (cur->_left == nullptr)
{
if (parent == nullptr) //防止最开始找到parent为空
{
_root = cur->_right;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_right;
}
else if (parent->_right == cur)
{
parent->_right = cur->_right;
}
}
delete cur;
}
//只有右为空
else if (cur->_right == nullptr)
{
if (parent == nullptr) //防止最开始找到parent为空
{
_root = cur->_left;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else if (parent->_right == cur)
{
parent->_right = cur->_left;
}
}
delete cur;
}
//左右都不为空, 找右子树的最左结点代替
else
{
parent = cur;
Node* p = cur->_right;
while (p->_left) //找紧挨着的结点作替换
{
parent = p;
p = p->_left;
}
cur->_key = p->_key;
if (parent->_left == p)
parent->_left = p->_right;
else if (parent->_right == p)
parent->_right = p->_right;
delete p;
}
}
return true;
}
}
return false;
}
递归版本:该思路很巧妙,注意学习
bool EErase(const K& key)
{
return _Erase(_root, key);
}
bool _Erase(Node*& root, const K& key)
{
//先找key
if (root == nullptr)
{
return false;
}
if (root->_key > key)
{
return _Erase(root->_left, key);
}
else if (root->_key < key)
{
return _Erase(root->_right, key);
}
else
{
//找到了
Node* del = root;
if (root->_left == nullptr)
{
root = root->_right;
}
else if (root->_right == nullptr)
{
root = root->_left;
}
else
{
// 左右都不为空,找左子树最右边的
Node* minNode = root->_left;
while (minNode->_right)
{
minNode = minNode->_right;
}
swap(root->_key, minNode->_key);
//交换完删再删除结点,精华部分
return _Erase(root->_left, key);
}
delete del;
return true;
}
}
默认成员函数
//构造函数
//BSTree():_root(nullptr){}
BSTree() = default; //强制生成默认构造函数
//拷贝构造
BSTree(const BSTree<K>& t)
{
_root = Copy(t._root);
}
//前序遍历拷贝构造!!
Node* Copy(Node* root)
{
if (root == nullptr)
{
return nullptr;
}
Node *newRoot = new Node(root->_key);
newRoot->_left = Copy(root->_left);
newRoot->_right = Copy(root->_right);
return newRoot;
}
//赋值运算符重载
BSTree<K>& operator=(BSTree<K> t)
{
swap(_root, t._root);
return *this;
}
//析构
~BSTree()
{
Destory(_root);
}
void Destory(Node*& root)
{
//后续递归
if (root == nullptr)
{
return;
}
Destory(root->_left);
Destory(root->_right);
delete root;
root = nullptr;
}
完整代码
//BSTree.h
#pragma once
//创建二叉树结点
template<class K>
struct BSTNode
{
struct BSTNode<K>* _left;
struct BSTNode<K>* _right;
K _key;
//构造函数
BSTNode(const K& key)
:_key(key)
,_left(nullptr)
,_right(nullptr)
{}
};
template<class K>
class BSTree
{
typedef BSTNode<K> Node;
public:
//BSTree():_root(nullptr){}
BSTree() = default; //强制生成默认构造函数
//拷贝构造
BSTree(const BSTree<K>& t)
{
_root = Copy(t._root);
}
//赋值运算符重载
BSTree<K>& operator=(BSTree<K> t)
{
swap(_root, t._root);
return *this;
}
~BSTree()
{
Destory(_root);
}
//插入
bool Insert(const K& key)
{
//空
if (nullptr == _root)
{
_root = new Node(key);
return true;
}
//不空
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
//找到插入位置
if (parent->_key > key)
{
//左边插入
parent->_left = new Node(key);
}
else
{
//右边插入
parent->_right = new Node(key);
}
return true;
}
bool EInsert(const K& key)
{
return _EInsert(_root, key);
}
bool EFind(const K& key)
{
return _EFind(_root, key);
}
bool EErase(const K& key)
{
return _Erase(_root, key);
}
//查找
bool Find(const 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(const 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 && cur->_right == nullptr)
{
if (cur == _root)
{
_root = nullptr;
}
else if (parent->_left == cur)
{
parent->_left = nullptr;
}
else if (parent->_right == cur)
{
parent->_right = nullptr;
}
delete cur;
}
else //不是叶子
{
//只有左为空
if (cur->_left == nullptr)
{
if (parent == nullptr) //防止最开始找到parent为空
{
_root = cur->_right;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_right;
}
else if (parent->_right == cur)
{
parent->_right = cur->_right;
}
}
delete cur;
}
//只有右为空
else if (cur->_right == nullptr)
{
if (parent == nullptr) //防止最开始找到parent为空
{
_root = cur->_left;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else if (parent->_right == cur)
{
parent->_right = cur->_left;
}
}
delete cur;
}
//左右都不为空, 找右子树的最左结点代替
else
{
parent = cur;
Node* p = cur->_right;
while (p->_left)
{
parent = p;
p = p->_left;
}
cur->_key = p->_key;
if (parent->_left == p)
parent->_left = p->_right;
else if (parent->_right == p)
parent->_right = p->_right;
delete p;
}
}
return true;
}
}
return false;
}
void InOrder()
{
_InOrder(_root);
}
protected:
void Destory(Node*& root)
{
//后续递归
if (root == nullptr)
{
return;
}
Destory(root->_left);
Destory(root->_right);
delete root;
root = nullptr;
}
Node* Copy(Node* root)
{
if (root == nullptr)
{
return nullptr;
}
Node *newRoot = new Node(root->_key);
newRoot->_left = Copy(root->_left);
newRoot->_right = Copy(root->_right);
return newRoot;
}
void _InOrder(const Node* root)
{
if (root == nullptr) return;
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
bool _EFind(Node* root, const K& key)
{
if (root == nullptr)
return false;
if (root->_key > key)
{
return _EFind(root->_left, key);
}
else if (root->_key < key)
{
return _EFind(root->_right, key);
}
else
{
return true;
}
}
//指针的引用!!!
bool _EInsert(Node*& root, const K& key)
{
if (root == nullptr)
{
root = new Node(key);
return true;
}
if (root->_key > key)
return _EInsert(root->_left, key);
else if (root->_key < key)
return _EInsert(root->_right, key);
else //相等
{
return false;
}
}
bool _Erase(Node*& root, const K& key)
{
//先找key
if (root == nullptr)
{
return false;
}
if (root->_key > key)
{
return _Erase(root->_left, key);
}
else if (root->_key < key)
{
return _Erase(root->_right, key);
}
else
{
//找到了
Node* del = root;
if (root->_left == nullptr)
{
root = root->_right;
}
else if (root->_right == nullptr)
{
root = root->_left;
}
else
{
// 左右都不为空,找左子树最右边的
Node* minNode = root->_left;
while (minNode->_right)
{
minNode = minNode->_right;
}
swap(root->_key, minNode->_key);
//交换完删再删除结点
return _Erase(root->_left, key);
}
delete del;
return true;
}
}
private:
Node* _root = nullptr;
};
BSTree性能分析
最好的情况如左图:插入删除效率都是O(logn)
最坏的情况如右图:插入删除效率是O(n),这样就失去了二叉树的优点。
因此后面会介绍AVL树和红黑树!!