搜索二叉树(Binary Search Tree,简称BST)是一种特殊的二叉树,它满足任一节点的左子树中的所有节点的值都小于该节点的值,右子树中的所有节点的值都大于该节点的值。这使得BST在查找、插入和删除操作上具有较高的效率。本文将详细介绍如何从零开始实现一个搜索二叉树,包括其节点结构的定义和一系列基本操作的实现。
BSTreeNode类的定义
首先,我们需要定义树的节点BSTreeNode
,它将作为树的基础结构:
template<class K, class V>
class BSTreeNode {
public:
K _key; // 节点存储的键
V _value; // 节点存储的值
BSTreeNode* _left; // 左子节点
BSTreeNode* _right; // 右子节点
BSTreeNode(const K& key, const V& value)
: _key(key), _value(value), _left(nullptr), _right(nullptr) {}
};
BSTree类的实现
接着,我们来实现BSTree
类,它包含了一系列操作二叉搜索树的方法:
插入操作
插入操作Insert
的核心思想是比较待插入节点的键与当前节点的键,决定向左子树还是右子树递归地进行插入操作。
bool Insert(const K& key, const V& value) {
if (!_root) {
_root = new Node(key, value);
return true;
}
Node* parent = nullptr;
Node* current = _root;
while (current) {
parent = current;
if (key < current->_key) {
current = current->_left;
}
else if (key > current->_key) {
current = current->_right;
}
else {
return false; // key已经存在
}
}
if (key < parent->_key) {
parent->_left = new Node(key, value);
}
else {
parent->_right = new Node(key, value);
}
return true;
}
查找操作
查找操作Find
通过逐层比较键值,沿树向下移动,直到找到匹配的节点或达到叶子节点。
Node* Find(const K& key) {
Node* current = _root;
while (current) {
if (key < current->_key) {
current = current->_left;
}
else if (key > current->_key) {
current = current->_right;
}
else {
return current; // 找到了
}
}
return nullptr; // 没有找到
}
删除操作
删除操作Erase
是最复杂的,它需要处理三种情况:被删除节点是叶子节点、只有一个子节点、有两个子节点。
- 被删除节点是叶子节点:直接删除该节点,并将其父节点的相应指针设置为
nullptr
。 - 被删除节点只有一个子节点:删除该节点,并将其父节点的相应指针指向其子节点。
- 被删除节点有两个子节点:找到该节点的中序后继节点(右子树中的最小节点),用它来替换被删除节点的键和值,然后删除中序后继节点。
下面是Erase
函数的实现代码:
bool Erase(const K& key) {
Node* parent = nullptr;
Node* current = _root;
// 查找key对应的节点,同时记录其父节点
while (current != nullptr && current->_key != key) {
parent = current;
if (key < current->_key) {
current = current->_left;
}
else {
current = current->_right;
}
if (current == nullptr) {
// 没有找到对应的节点
return false;
}
}
//case1&2: 没有孩子或者只有一个孩子
if (current->_left == nullptr || current->_right == nullptr) {
Node* child = (current->_left == nullptr) ? current->_right : current->_left;
if (parent == nullptr) {
// 删除的是根节点
_root = child;
}
else {
if (parent->_left == current) {
parent->_left = child;
}
else {
parent->_right = child;
}
}
delete current;
}
else {
// case 3: 两个孩子
// 找到右子树最小节点, 即中序后继节点
Node* successor = current->_right;
Node* successorParent = current;
while (successor->_left != nullptr) {
successorParent = successor;
successor = successor->_left;
}
// 替换current 的节点和键值
current->_key = successor->_key;
current->_value = successor->_value;
if (successorParent->_left = successor) {
successorParent->_left = successor->_right;
}
else {
successorParent->_right = successor->_right;
}
delete successor;
}
}
中序遍历
中序遍历InOrder
按照左子树-根节点-右子树的顺序访问树中的每个节点,对于BST来说,这将以升序方式访问所有节点。
void _InOrder(Node* root) {
if (!root) return;
_InOrder(root->_left);
cout << root->_key << " : " << root->_value << endl;
_InOrder(root->_right);
}
void InOrder() {
_InOrder(_root);
}
递归实现
对于插入、查找和删除操作,我们还提供了递归版本的实现方法,以更直观地展示这些操作的逻辑。
// 递归版本 //
bool FindR(const K& key) {
return _FindR(_root, key);
}
bool InsertR(const K& key, const V& value) {
return _InsertR(_root, key, value);
}
bool EraseR(const K& key) {
return _EraseR(_root, key);
}
private:
bool _FindR(Node* root, const K& key) {
if (root == nullptr)
return false;
if (root->_key < key) {
return _FindR(root->_right, key);
}
else if (root->_key > key) {
return _FindR(root->_left, key);
}
else {
return true;
}
};
bool _InsertR(Node*& root, const K& key, const V& value) {
if (root == nullptr) {
root = new Node(key, value);
return true;
}
if (root->_key < key) {
return _InsertR(root->_right, key, value);
}
else if (root->_key > key) {
return _InsertR(root->_left, key, value);
}
else {
return false;
}
}
bool _EraseR(Node*& root, const K& key) {
if (root == nullptr) {
return false;
}
if (root->_key < key) {
return _EraseR(root->_right, key);
}
else if (root->_key > key) {
return _EraseR(root->_left, key);
}
else {
//case1&2: 没有孩子或者只有一个孩子
Node* del = root;
if (root->_right == nullptr) {
root = root->_left;
}
else if (root->_left == nullptr){
root = root->_right;
}
else {
// case 3: 两个孩子
// 找到右子树最小节点,即中序后继节点
Node* successor = root->_right;
while (successor->_left != nullptr) {
successor = successor->_left;
}
// 将中序后继节点的值复制到当前节点
root->_key = successor->_key;
// 递归删除中序后继节点
_EraseR(root->_right, successor->_key);
}
delete del;
return true;
}
}
完整代码和测试
文章最后给出了BSTree
类的完整实现代码,并提供了几个简单的测试用例来验证我们的实现。
#pragma once
#include <iostream>
#include <string>
using namespace std;
template<class K, class V>
class BSTreeNode {
public:
K _key; // 节点存储的键
V _value; // 节点存储的值
BSTreeNode* _left; //左子节点
BSTreeNode* _right; //右子节点
BSTreeNode(const K& key, const V& value)
: _key(key), _value(value), _left(nullptr), _right(nullptr) {}
};
template<class K, class V>
class BSTree
{
typedef BSTreeNode<K, V> Node;
public:
//强制生成默认构造
BSTree() = default;
BSTree(const BSTree<K,V>& t) {
_root = Copy(t._root);
}
BSTree<K,V>& operator=(BSTree<K,V>& t) {
swap(_root, t._root);
}
Node* Copy(Node* root) {
if (root == nullptr) {
return nullptr;
}
Node* newRoot = new Node(root->_key, root->_value);
newRoot->_left = Copy(root->_left);
newRoot->_right = Copy(root->_right);
return newRoot;
}
~BSTree()
{
Destroy(_root);
}
void Destroy(Node* root) {
if (root == nullptr)
return;
Destroy(root->_left);
Destroy(root->_right);
delete root;
}
bool Insert(const K& key, const V& value) {
if (!_root) {
_root = new Node(key, value);
return true;
}
Node* parent = nullptr;
Node* current = _root;
while (current) {
parent = current;
if (key < current->_key) {
current = current->_left;
}
else if (key > current->_key) {
current = current->_right;
}
else {
return false; // key已经存在
}
}
if (key < parent->_key) {
parent->_left = new Node(key, value);
}
else {
parent->_right = new Node(key, value);
}
return true;
}
Node* Find(const K& key) {
Node* current = _root;
while (current) {
if (key < current->_key) {
current = current->_left;
}
else if (key > current->_key) {
current = current->_right;
}
else {
return current; // 找到了
}
}
return nullptr; // 没有找到
}
bool Erase(const K& key) {
Node* parent = nullptr;
Node* current = _root;
// 查找key对应的节点,同时记录其父节点
while (current != nullptr && current->_key != key) {
parent = current;
if (key < current->_key) {
current = current->_left;
}
else {
current = current->_right;
}
if (current == nullptr) {
// 没有找到对应的节点
return false;
}
}
//case1&2: 没有孩子或者只有一个孩子
if (current->_left == nullptr || current->_right == nullptr) {
Node* child = (current->_left == nullptr) ? current->_right : current->_left;
if (parent == nullptr) {
// 删除的是根节点
_root = child;
}
else {
if (parent->_left == current) {
parent->_left = child;
}
else {
parent->_right = child;
}
}
delete current;
}
else {
// case 3: 两个孩子
// 找到右子树最小节点, 即中序后继节点
Node* successor = current->_right;
Node* successorParent = current;
while (successor->_left != nullptr) {
successorParent = successor;
successor = successor->_left;
}
// 替换current 的节点和键值
current->_key = successor->_key;
current->_value = successor->_value;
if (successorParent->_left = successor) {
successorParent->_left = successor->_right;
}
else {
successorParent->_right = successor->_right;
}
delete successor;
}
}
void InOrder() {
_InOrder(_root);
}
// 递归版本 //
bool FindR(const K& key) {
return _FindR(_root, key);
}
bool InsertR(const K& key, const V& value) {
return _InsertR(_root, key, value);
}
bool EraseR(const K& key) {
return _EraseR(_root, key);
}
private:
bool _FindR(Node* root, const K& key) {
if (root == nullptr)
return false;
if (root->_key < key) {
return _FindR(root->_right, key);
}
else if (root->_key > key) {
return _FindR(root->_left, key);
}
else {
return true;
}
};
bool _InsertR(Node*& root, const K& key, const V& value) {
if (root == nullptr) {
root = new Node(key, value);
return true;
}
if (root->_key < key) {
return _InsertR(root->_right, key, value);
}
else if (root->_key > key) {
return _InsertR(root->_left, key, value);
}
else {
return false;
}
}
bool _EraseR(Node*& root, const K& key) {
if (root == nullptr) {
return false;
}
if (root->_key < key) {
return _EraseR(root->_right, key);
}
else if (root->_key > key) {
return _EraseR(root->_left, key);
}
else {
//case1&2: 没有孩子或者只有一个孩子
Node* del = root;
if (root->_right == nullptr) {
root = root->_left;
}
else if (root->_left == nullptr){
root = root->_right;
}
else {
// case 3: 两个孩子
// 找到右子树最小节点,即中序后继节点
Node* successor = root->_right;
while (successor->_left != nullptr) {
successor = successor->_left;
}
// 将中序后继节点的值复制到当前节点
root->_key = successor->_key;
// 递归删除中序后继节点
_EraseR(root->_right, successor->_key);
}
delete del;
return true;
}
}
void _InOrder(Node* root) {
if (!root) return;
_InOrder(root->_left);
cout << root->_key << " : " << root->_value << endl;
_InOrder(root->_right);
}
Node* _root = nullptr;
};
void TestBSTree1()
{
BSTree<string, string> dict;
dict.InsertR("insert", "插入");
dict.InsertR("erase", "删除");
dict.InsertR("left", "左边");
dict.InsertR("string", "字符串");
dict.InOrder();
string str;
while (cin >> str)
{
auto ret = dict.Find(str);
if (ret)
{
cout << str << ":" << ret->_value << endl;
}
else
{
cout << "单词拼写错误" << endl;
}
cout << "删除: " << ret->_key << " : " << ret->_value << endl;
dict.EraseR(ret->_key);
dict.InOrder();
}
}
void TestBSTree2()
{
string strs[] = { "苹果", "西瓜", "苹果", "樱桃", "苹果", "樱桃", "苹果", "樱桃", "苹果" };
// 统计水果出现的次
BSTree<string, int> countTree;
for (auto str : strs)
{
auto ret = countTree.Find(str);
if (ret == NULL)
{
countTree.Insert(str, 1);
}
else
{
ret->_value++;
}
}
countTree.InOrder();
}
void TestBSTree3() {
BSTree<int, string> t;
t.Insert(8, "八");
t.Insert(3, "三");
t.Insert(1, "一");
t.Insert(10, "十");
t.Insert(6, "六");
t.Insert(4, "四");
t.Insert(7, "七");
t.Insert(14, "十四");
t.Insert(13, "十三");
t.InOrder();
BSTree<int, string> t1(t);
t1.InOrder();
}
总结
通过本文的介绍,我们了解了二叉搜索树的基本概念、节点结构的定义以及如何实现插入、查找、删除和中序遍历等基本操作。实现一个搜索二叉树不仅能够加深对数据结构和算法的理解,也能够提升编程能力和问题解决能力。