#include <iostream>
struct TreeNode {
int value;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : value(x), left(nullptr), right(nullptr) {}
};
class BinarySearchTree {
public:
BinarySearchTree() : root(nullptr) {}
// 插入节点
void insert(int value) {
root = insertIntoBST(root, value);
}
// 删除节点
void remove(int value) {
root = removeFromBST(root, value);
}
// 中序遍历
void inorderTraversal() {
inorder(root);
std::cout << std::endl;
}
private:
TreeNode *root;
// 插入节点辅助函数
TreeNode* insertIntoBST(TreeNode* node, int value) {
if (node == nullptr) {
return new TreeNode(value);
}
if (value < node->value) {
node->left = insertIntoBST(node->left, value);
} else if (value > node->value) {
node->right = insertIntoBST(node->right, value);
}
return node;
}
// 删除节点辅助函数
TreeNode* removeFromBST(TreeNode* node, int value) {
if (node == nullptr) {
return nullptr;
}
if (value < node->value) {
node->left = removeFromBST(node->left, value);
} else if (value > node->value) {
node->right = removeFromBST(node->right, value);
} else {
// 找到待删除的节点
if (node->left == nullptr) {
TreeNode* rightChild = node->right;
delete node;
return rightChild;
} else if (node->right == nullptr) {
TreeNode* leftChild = node->left;
delete node;
return leftChild;
} else {
// 节点有两个孩子,找到右子树中的最小节点
TreeNode* minNode = findMin(node->right);
// 用右子树最小节点的值替换当前节点的值
node->value = minNode->value;
// 删除右子树中的最小节点
node->right = removeFromBST(node->right, minNode->value);
}
}
return node;
}
// 找到最小节点
TreeNode* findMin(TreeNode* node) {
while (node->left != nullptr) {
node = node->left;
}
return node;
}
// 中序遍历辅助函数
void inorder(TreeNode* node) {
if (node == nullptr) {
return;
}
inorder(node->left);
std::cout << node->value << " ";
inorder(node->right);
}
};
int main() {
BinarySearchTree bst;
bst.insert(50);
bst.insert(30);
bst.insert(20);
bst.insert(40);
bst.insert(70);
bst.insert(60);
bst.insert(80);
std::cout << "Original tree: ";
bst.inorderTraversal();
bst.remove(20);
std::cout << "After removing 20: ";
bst.inorderTraversal();
bst.remove(30);
std::cout << "After removing 30: ";
bst.inorderTraversal();
bst.remove(50);
std::cout << "After removing 50: ";
bst.inorderTraversal();
return 0;
}
在C++的代码中使用“辅助函数”(helper functions),尤其是将它们定义在private
部分,而不是直接在public
部分暴露所有功能,有几个重要的原因,这涉及到代码的可读性、可维护性以及安全性等方面。让我们详细解释一下这些原因。
1. 封装性(Encapsulation)和数据隐藏
将辅助函数放在private
部分是一种封装性的体现。封装是面向对象编程(OOP)的核心原则之一。它允许对象将某些数据和实现细节隐藏起来,只暴露出有限的接口给外部使用者。
-
保护内部数据结构和算法的实现细节:通过将插入节点和删除节点的辅助函数(如
insertIntoBST
和removeFromBST
)放在private
部分,BinarySearchTree
类的使用者不需要关心或访问这些函数的实现细节,只需使用提供的insert
和remove
这些public
接口函数即可。这样做可以减少对类内部实现的依赖,降低代码的耦合度。 -
数据完整性和一致性:通过限制对树的直接操作(例如直接操作树的节点指针),可以防止外部代码在不知不觉中破坏树的内部结构或违反二叉搜索树的性质(如树的有序性)。
2. 代码的可读性和可维护性
在public
接口中只保留最重要的函数,如insert
、remove
和inorderTraversal
,使代码的接口更简洁清晰,便于使用者理解和使用。
-
提高可读性:在
public
部分放入过多的辅助函数,会增加代码的复杂度,使得类的接口变得杂乱无章,使用者不容易找到实际需要用到的函数。相反,将这些辅助函数移到private
部分后,类的公共接口变得简洁、清晰。 -
方便修改和扩展:如果将来需要修改插入或删除的逻辑,开发者可以直接在
private
部分修改相应的辅助函数,而不需要担心这些函数被外部直接调用。这使得代码的维护和扩展变得更加容易。
3. 安全性
虽然在C++中,private
并不完全阻止访问(通过指针操作等仍有可能访问私有成员),但它确实增加了对内部数据结构的访问难度,从而增加了一些安全性。
- 防止滥用或误用:将这些辅助函数设为
private
,可以防止外部代码在未理解函数行为的情况下误用,导致潜在的bug或逻辑错误。例如,直接操作树的节点指针,可能会导致树的结构被破坏,而不知不觉中违反了二叉搜索树的性质。
4. 模块化设计
通过这种方式进行模块化设计,可以将复杂的逻辑分解为更小的部分,方便独立开发和调试。每个辅助函数仅负责一个小的逻辑功能,使得代码更具模块化,易于测试和调试。
总结
将insertIntoBST
、removeFromBST
这些辅助函数定义在private
部分,而提供的insert
和remove
等public
函数作为对外接口,这是一种良好的编码实践。它不仅保护了类内部的数据结构和算法的实现细节,还提高了代码的可读性、可维护性和安全性,符合面向对象编程的设计原则。