目录
一. 二叉搜索树的结构
二. 二叉搜索树的模拟实现
2.1 二叉搜索树的节点及类的成员变量
2.2 数据插入操作Insert的实现
2.3 数据删除操作Erase的实现
2.4 数据查找操作Find的实现
2.5 中序遍历InOrder的实现
2.6 构造函数的实现
2.7 析构函数的实现
三. 二叉搜索树的应用
3.1 二叉搜索树的key模型
3.2 二叉搜索树的key-value模型
四. 二叉搜索树的性能分析
附录一:二叉搜索树的Key模型完整版代码
附录二:二叉搜索树的key-value模型完整版代码
二叉搜索树是一种特殊的二叉树,它要么为空树,要么具有一下的结构特点:
在理想情况下,二叉搜索树可以实现时间复杂度为O(logN)的查找操作。
以struct类的形式来定义二叉搜索树的节点,每个节点包含3个成员,分别为:
将class BSTree定义为二叉搜索树模板板类,将模板参数定义为K -- template
注:带有前缀 _ 的private属性函数,为被相应成员函数调用的子函数,本文将BSTree模板类声明定义在命名空间zhang中。
代码2.1:(节点和BSTree模板类的声明)
template
struct BSTreeNode //二叉搜索树节点
{
BSTreeNode* _left;
BSTreeNode* _right;
K _key;
BSTreeNode(const K& key)
: _left(nullptr)
, _right(nullptr)
, _key(key)
{}
};
template
class BSTree
{
typedef BSTreeNode Node;
public:
BSTree() = default; //强制编译器生成默认构造函数
BSTree(const BSTree& bt); //拷贝构造函数
~BSTree(); //析构函数
bool Insert(const K& x); //数据插入函数
Node* Find(const K& key); //二叉搜索树节点查找函数
void InOrder(); //中序遍历函数
bool Erase(const K& x); //节点删除函数
bool InsertR(const K& x); //递归插入函数
bool EraseR(const K& x); //递归删除数据函数
Node* FindR(const K& key); //递归查找函数
private:
Node* _Copy(Node* root);
void _Destroy(Node*& root);
void _InOrder(Node* root);
bool _InsertR(Node*& root, const K& x);
bool _EraseR(Node*& root, const K& x);
Node* _FindR(Node* root, const K& key);
private:
Node* _root = nullptr;
};
Insert的非递归实现方法:
Insert的递归实现方法:
代码2.2:(数据插入的非递归实现和递归实现函数)
//搜索二叉树节点非递归插入函数
template
bool zhang::BSTree::Insert(const K& x)
{
if (_root == nullptr)
{
//处理根节点为空(树中没有节点的情况)
_root = new Node(x);
return false;
}
Node* cur = _root;
Node* parent = nullptr; //插入节点的父亲节点
while (cur)
{
parent = cur;
if (cur->_key > x)
{
//节点值大于插入数据向左移
cur = cur->_left;
}
else if (cur->_key < x)
{
//节点值小于插入数据向右移
cur = cur->_right;
}
else
{
//二叉搜索树不允许有重复的节点,如果出现插入节点值和某一节点相同,则插入失败
return false;
}
}
cur = new Node(x);
if (parent->_key < x)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
return true;
}
//递归插入函数
template
bool zhang::BSTree::InsertR(const K& x)
{
return _InsertR(_root, x);
}
template
bool zhang::BSTree::_InsertR(Node*& root, const K& x)
{
if (root == nullptr)
{
root = new Node(x);
}
if (root->_key > x)
{
return _InsertR(root->_left, x);
}
else if(root->_key < x)
{
return _InsertR(root->_right, x);
}
else
{
return false;
}
}
删除某个特定的数据的具体方法,应当分为以下三种情况来讨论:
在编写代码是,可将第1、第2种情况合为一体编码。
对于Erase操作的递归实现,如果递归到要删除的节点,判断要删除的节点是否有两个节点,如果没有,只需要将被删节点的子节点直接赋值给本层root即可,如果有,那么执行与非递归实现中相同的节点值互换操作,再递归删除子树中被换的节点即可。
代码2.3:(删除节点的非递归实现和递归实现)
//节点删除函数
template
bool zhang::BSTree::Erase(const K& x)
{
Node* parent = nullptr; //被删除节点的父亲节点
Node* cur = _root;
while (cur)
{
if (cur->_key < x)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > x)
{
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;
cur = nullptr;
}
else if(cur->_right == nullptr)
{
if (parent == nullptr) //要删除的节点为根节点
{
_root = cur->_left;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
cur = nullptr;
}
else
{
//要删除节点的左子节点和右子节点均不为nullptr
//交换删除节点与右子树最左侧节点的值
Node* leftInRight = cur->_right;
Node* parentInRight = cur;
//找右子树的最左节点
while (leftInRight->_left != nullptr)
{
parentInRight = leftInRight;
leftInRight = leftInRight->_left;
}
std::swap(leftInRight->_key, cur->_key);
if (parentInRight == cur)
{
parentInRight->_right = leftInRight->_right;
}
else
{
parentInRight->_left = leftInRight->_right;
}
delete leftInRight;
leftInRight = nullptr;
return true;
}
}
}
return false;
}
//递归删除数据函数
template
bool zhang::BSTree::EraseR(const K& x)
{
return _EraseR(_root, x);
}
template
bool zhang::BSTree::_EraseR(Node*& root, const K& x)
{
if (root == nullptr)
{
return false;
}
if (root->_key > x)
{
return _EraseR(root->_left, x);
}
else if (root->_key < x)
{
return _EraseR(root->_right, x);
}
else
{
//删除数据
Node* del = root; //带删除的节点
if (root->_left == nullptr)
{
root = del->_right;
}
else if (root->_right == nullptr)
{
root = del->_left;
}
else
{
//要删除的节点左右子节点都不为空
//去找右子树最小的节点,交换删除
Node* minRight = root->_right;
while (minRight->_left)
{
minRight = minRight->_left;
}
std::swap(root->_key, minRight->_key);
return _EraseR(root->_right, x);
}
delete del;
del = nullptr;
return true;
}
}
将节点数据val与要查找的数据key进行比较,如果val小于key,那么到这个节点的右子树去查找,如果val大于key,那么去这个节点的左子树去查找,如果两者相等,那么返回该节点的指针。如果遇到了nullptr,那么表明二叉搜索树中没有key,返回nullptr。
代码2.4:(查找的非递归实现和递归实现)
//递归查找函数
template
zhang::BSTreeNode* zhang::BSTree::FindR(const K& key)
{
return _FindR(_root, key);
}
template
zhang::BSTreeNode* zhang::BSTree::_FindR(Node* root, const K& key)
{
if (nullptr == root)
{
return nullptr;
}
if (root->_key > key)
{
return _FindR(root->_left, key);
}
else if (root->_key < key)
{
return _FindR(root->_right, key);
}
else
{
return root;
}
}
//搜索二叉树节点查找函数非递归实现
template
zhang::BSTreeNode* zhang::BSTree::Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key == key)
{
return cur; //找到节点
}
else if (cur->_key < key)
{
//向右子树查找
cur = cur->_right;
}
else
{
//向左子树查找
cur = cur->_left;
}
}
return nullptr;
}
由于二叉搜索树的左子树的节点均小于根节点,右子树的节点均大于根节点,因此,搜索二叉树中序遍历可以获取一组无重复的升序序列。其实现方法与普通二叉树的中序遍历一致。
代码2.5:(二叉搜索树中序遍历)
//搜索二叉树中序遍历函数(升序)
template
void zhang::BSTree::InOrder()
{
_InOrder(_root);
std::cout << std::endl;
}
template
void zhang::BSTree::_InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
std::cout << root->_key << " ";
_InOrder(root->_right);
}
注:如果显示定义了拷贝构造函数,那么编译器在一般情况下就不会自动生成默认构造函数。此时,应当使用下面的指令来强制编译器生成默认构造函数:
BSTree() = default; //强制编译器生成默认构造函数
代码2.6:(拷贝构造函数)
//拷贝构造函数
template
zhang::BSTree::BSTree(const BSTree& bt)
{
_root = _Copy(bt._root);
}
template
zhang::BSTreeNode* zhang::BSTree::_Copy(Node* root)
{
if (root == nullptr)
{
return nullptr;
}
Node* newNode = new Node(root->_key);
newNode->_left = _Copy(root->_left);
newNode->_right = _Copy(root->_right);
return newNode;
}
按照后序遍历的方式,依次释放每个节点即可。
代码2.7:(析构函数)
//析构函数
template
zhang::BSTree::~BSTree()
{
_Destroy(_root);
}
template
void zhang::BSTree::_Destroy(Node*& root)
{
if (root == nullptr)
{
return;
}
_Destroy(root->_left);
_Destroy(root->_right);
delete root;
root = nullptr;
}
key模型,是查找模型,第二章中所实现的就是搜索二叉树的key模型,其只存储一个key值作为关键码,关键码为用于搜索的值。
每个节点中存储一个关键码key和一个与之对于的value值,即存储
由此可见,二叉搜索树的结构对查找效率存在显著影响,这依赖于数据插入的顺序。通过使用AVL树和红黑树,可以避免这一缺陷。
namespace zhang
{
template
struct BSTreeNode //二叉搜索树节点
{
BSTreeNode* _left;
BSTreeNode* _right;
K _key;
BSTreeNode(const K& key)
: _left(nullptr)
, _right(nullptr)
, _key(key)
{}
};
template
class BSTree
{
typedef BSTreeNode Node;
public:
BSTree() = default; //强制编译器生成默认构造函数
BSTree(const BSTree& bt); //拷贝构造函数
~BSTree(); //析构函数
bool Insert(const K& x); //数据插入函数
Node* Find(const K& key); //二叉搜索树节点查找函数
void InOrder(); //中序遍历函数
bool Erase(const K& x); //节点删除函数
bool InsertR(const K& x); //递归插入函数
bool EraseR(const K& x); //递归删除数据函数
Node* FindR(const K& key); //递归查找函数
private:
Node* _Copy(Node* root);
void _Destroy(Node*& root);
void _InOrder(Node* root);
bool _InsertR(Node*& root, const K& x);
bool _EraseR(Node*& root, const K& x);
Node* _FindR(Node* root, const K& key);
private:
Node* _root = nullptr;
};
}
//拷贝构造函数
template
zhang::BSTree::BSTree(const BSTree& bt)
{
_root = _Copy(bt._root);
}
template
zhang::BSTreeNode* zhang::BSTree::_Copy(Node* root)
{
if (root == nullptr)
{
return nullptr;
}
Node* newNode = new Node(root->_key);
newNode->_left = _Copy(root->_left);
newNode->_right = _Copy(root->_right);
return newNode;
}
//析构函数
template
zhang::BSTree::~BSTree()
{
_Destroy(_root);
}
template
void zhang::BSTree::_Destroy(Node*& root)
{
if (root == nullptr)
{
return;
}
_Destroy(root->_left);
_Destroy(root->_right);
delete root;
root = nullptr;
}
//递归查找函数
template
zhang::BSTreeNode* zhang::BSTree::FindR(const K& key)
{
return _FindR(_root, key);
}
template
zhang::BSTreeNode* zhang::BSTree::_FindR(Node* root, const K& key)
{
if (nullptr == root)
{
return nullptr;
}
if (root->_key > key)
{
return _FindR(root->_left, key);
}
else if (root->_key < key)
{
return _FindR(root->_right, key);
}
else
{
return root;
}
}
//递归删除数据函数
template
bool zhang::BSTree::EraseR(const K& x)
{
return _EraseR(_root, x);
}
template
bool zhang::BSTree::_EraseR(Node*& root, const K& x)
{
if (root == nullptr)
{
return false;
}
if (root->_key > x)
{
return _EraseR(root->_left, x);
}
else if (root->_key < x)
{
return _EraseR(root->_right, x);
}
else
{
//删除数据
Node* del = root; //带删除的节点
if (root->_left == nullptr)
{
root = del->_right;
}
else if (root->_right == nullptr)
{
root = del->_left;
}
else
{
//要删除的节点左右子节点都不为空
//去找右子树最小的节点,交换删除
Node* minRight = root->_right;
while (minRight->_left)
{
minRight = minRight->_left;
}
std::swap(root->_key, minRight->_key);
return _EraseR(root->_right, x);
}
delete del;
del = nullptr;
return true;
}
}
//递归插入函数
template
bool zhang::BSTree::InsertR(const K& x)
{
return _InsertR(_root, x);
}
template
bool zhang::BSTree::_InsertR(Node*& root, const K& x)
{
if (root == nullptr)
{
root = new Node(x);
}
if (root->_key > x)
{
return _InsertR(root->_left, x);
}
else if(root->_key < x)
{
return _InsertR(root->_right, x);
}
else
{
return false;
}
}
//搜索二叉树节点插入函数
template
bool zhang::BSTree::Insert(const K& x)
{
if (_root == nullptr)
{
//处理根节点为空(树中没有节点的情况)
_root = new Node(x);
return false;
}
Node* cur = _root;
Node* parent = nullptr; //插入节点的父亲节点
while (cur)
{
parent = cur;
if (cur->_key > x)
{
//节点值大于插入数据向左移
cur = cur->_left;
}
else if (cur->_key < x)
{
//节点值小于插入数据向右移
cur = cur->_right;
}
else
{
//二叉搜索树不允许有重复的节点,如果出现插入节点值和某一节点相同,则插入失败
return false;
}
}
cur = new Node(x);
if (parent->_key < x)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
return true;
}
//搜索二叉树节点查找函数
template
zhang::BSTreeNode* zhang::BSTree::Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key == key)
{
return cur; //找到节点
}
else if (cur->_key < key)
{
//向右子树查找
cur = cur->_right;
}
else
{
//向左子树查找
cur = cur->_left;
}
}
return nullptr;
}
//搜索二叉树中序遍历函数(升序)
template
void zhang::BSTree::InOrder()
{
_InOrder(_root);
std::cout << std::endl;
}
template
void zhang::BSTree::_InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
std::cout << root->_key << " ";
_InOrder(root->_right);
}
//节点删除函数
template
bool zhang::BSTree::Erase(const K& x)
{
Node* parent = nullptr; //被删除节点的父亲节点
Node* cur = _root;
while (cur)
{
if (cur->_key < x)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > x)
{
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;
cur = nullptr;
}
else if(cur->_right == nullptr)
{
if (parent == nullptr) //要删除的节点为根节点
{
_root = cur->_left;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
cur = nullptr;
}
else
{
//要删除节点的左子节点和右子节点均不为nullptr
//交换删除节点与右子树最左侧节点的值
Node* leftInRight = cur->_right;
Node* parentInRight = cur;
//找右子树的最左节点
while (leftInRight->_left != nullptr)
{
parentInRight = leftInRight;
leftInRight = leftInRight->_left;
}
std::swap(leftInRight->_key, cur->_key);
if (parentInRight == cur)
{
parentInRight->_right = leftInRight->_right;
}
else
{
parentInRight->_left = leftInRight->_right;
}
delete leftInRight;
leftInRight = nullptr;
return true;
}
}
}
return false;
}
namespace zhang
{
template
struct BSTreeNode //二叉搜索树节点
{
BSTreeNode* _left; //左子节点
BSTreeNode* _right; //右子节点
K _key; //关键字
V _val; //映射值
BSTreeNode(const K& key, const V& val)
: _left(nullptr)
, _right(nullptr)
, _key(key)
, _val(val)
{ }
};
template
class BSTree
{
typedef BSTreeNode Node;
public:
bool Insert(const K& key, const V& val); //数据插入函数
void InOrder(); //中序遍历函数
bool Erase(const K& key); //数据删除函数
Node* find(const K& key); //查找函数
private:
Node* _find(Node* root, const K& key);
void _InOrder(Node* root);
private:
Node* _root = nullptr; //根节点
};
}
//查找函数
template
zhang::BSTreeNode* zhang::BSTree::find(const K& key)
{
return _find(_root, key);
}
template
zhang::BSTreeNode* zhang::BSTree::_find(Node* root, const K& key)
{
if (root == nullptr)
{
return nullptr;
}
if (root->_key > key)
{
return _find(root->_left, key);
}
else if (root->_key < key)
{
return _find(root->_right, key);
}
else
{
//std::cout << key << "->" << root->_val << std::endl;
return root;
}
}
template
bool zhang::BSTree::Erase(const K& key)
{
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
{
//开始删除数据
if (cur->_left == nullptr)
{
if (parent == nullptr)
{
_root = cur->_right; //删除了根节点
delete cur;
}
else
{
//删除非根节点
if (parent->_left == cur)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
delete cur;
}
}
else if (cur->_right == nullptr)
{
if (parent == nullptr)
{
_root = cur->_left; //删除了根节点
delete cur;
}
else
{
//删除非根节点
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
delete cur;
}
}
else
{
//要删除的节点的左右子节点均不为空
//1.找右子树中最小的节点
Node* minRight = cur->_right;
Node* parentMin = cur;
while (minRight->_left)
{
parentMin = minRight;
minRight = minRight->_left;
}
std::swap(cur->_key, minRight->_key);
std::swap(cur->_val, minRight->_val);
if (parentMin == cur)
{
parentMin->_right = minRight->_right;
}
else
{
parentMin->_left = minRight->_right;
}
delete minRight;
}
return true;
}
}
return false;
}
template
void zhang::BSTree::InOrder()
{
_InOrder(_root);
std::cout << std::endl;
}
template
void zhang::BSTree::_InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
std::cout << "<" << root->_key << "," << root->_val << ">" << " ";
_InOrder(root->_right);
}
template
bool zhang::BSTree::Insert(const K& key, const V& val)
{
if (_root == nullptr) //二叉树为空
{
_root = new Node(key, val);
return true;
}
//二叉搜索树不为空
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
{
//二叉搜索树中不允许有相同节点,遇到相同节点插入失败,返回false
return false;
}
}
//判断应该插入到左子树还是右子树
if (parent->_key < key)
{
parent->_right = new Node(key, val);
}
else
{
parent->_left = new Node(key, val);
}
return true;
}