简单二叉搜索树案例实现,体会面向对象特性、函数封装以及辅助函数的使用

#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)的核心原则之一。它允许对象将某些数据和实现细节隐藏起来,只暴露出有限的接口给外部使用者。

  • 保护内部数据结构和算法的实现细节:通过将插入节点和删除节点的辅助函数(如insertIntoBSTremoveFromBST)放在private部分,BinarySearchTree类的使用者不需要关心或访问这些函数的实现细节,只需使用提供的insertremove这些public接口函数即可。这样做可以减少对类内部实现的依赖,降低代码的耦合度。

  • 数据完整性和一致性:通过限制对树的直接操作(例如直接操作树的节点指针),可以防止外部代码在不知不觉中破坏树的内部结构或违反二叉搜索树的性质(如树的有序性)。

2. 代码的可读性和可维护性

public接口中只保留最重要的函数,如insertremoveinorderTraversal,使代码的接口更简洁清晰,便于使用者理解和使用。

  • 提高可读性:在public部分放入过多的辅助函数,会增加代码的复杂度,使得类的接口变得杂乱无章,使用者不容易找到实际需要用到的函数。相反,将这些辅助函数移到private部分后,类的公共接口变得简洁、清晰。

  • 方便修改和扩展:如果将来需要修改插入或删除的逻辑,开发者可以直接在private部分修改相应的辅助函数,而不需要担心这些函数被外部直接调用。这使得代码的维护和扩展变得更加容易。

3. 安全性

虽然在C++中,private并不完全阻止访问(通过指针操作等仍有可能访问私有成员),但它确实增加了对内部数据结构的访问难度,从而增加了一些安全性。

  • 防止滥用或误用:将这些辅助函数设为private,可以防止外部代码在未理解函数行为的情况下误用,导致潜在的bug或逻辑错误。例如,直接操作树的节点指针,可能会导致树的结构被破坏,而不知不觉中违反了二叉搜索树的性质。

4. 模块化设计

通过这种方式进行模块化设计,可以将复杂的逻辑分解为更小的部分,方便独立开发和调试。每个辅助函数仅负责一个小的逻辑功能,使得代码更具模块化,易于测试和调试。

总结

insertIntoBSTremoveFromBST这些辅助函数定义在private部分,而提供的insertremovepublic函数作为对外接口,这是一种良好的编码实践。它不仅保护了类内部的数据结构和算法的实现细节,还提高了代码的可读性、可维护性和安全性,符合面向对象编程的设计原则。

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值