文章目录
- BSTreeNode类的定义
- BSTree类的实现
- 插入操作
- 查找操作
- 删除操作
- 中序遍历
- 递归实现
- 完整代码和测试
- 总结
搜索二叉树(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();
}
总结
通过本文的介绍,我们了解了二叉搜索树的基本概念、节点结构的定义以及如何实现插入、查找、删除和中序遍历等基本操作。实现一个搜索二叉树不仅能够加深对数据结构和算法的理解,也能够提升编程能力和问题解决能力。