二分搜索树

2023-11-03

经典写法(内心os:就是有点繁琐hh~)

#include <iostream>
#include <queue>
#include <cassert>
using namespace std;

template <typename Key, typename Value>
class BST
{
private:
    struct Node
    {
        Key key;
        Value value;
        Node *left;
        Node *right;

        Node(Key key, Value value)
        {
            this->key = key;
            this->value = value;
            this->left = this->right = NULL;
        }

        Node(Node *node)
        {
            this->key = node->key;
            this->value = node->value;
            this->left = node->left;
            this->right = node->right;
        }
    };

    Node *root;
    int count;

    // 用于判断是否为二叉搜索树的变量
    Node *pre = nullptr;
    bool res = true;

public:
    BST()
    {
        root = NULL;
        count = 0;
    }
    ~BST()
    {
        _destroy(root);
    }

    int size()
    {
        return count;
    }

    bool isEmpty()
    {
        return count == 0;
    }

    void insert(Key key, Value value)
    {
        root = _insert(root, key, value);
    }

    bool contain(Key key)
    {
        return _contain(root, key);
    }

    Value *search(Key key)
    {
        return _search(root, key);
    }

    // 前序遍历
    void preOrder()
    {
        _preOrder(root);
    }

    // 中序遍历
    void inOrder()
    {
        _inOrder(root);
    }

    // 后序遍历
    void postOrder()
    {
        _postOrder(root);
    }

    // 层序遍历
    void levelOrder()
    {
        queue<Node *> q;
        q.push(root);
        while (!q.empty())
        {
            Node *node = q.front();
            q.pop();

            cout << node->key << " ";
            if (node->left)
                q.push(node->left);
            if (node->right)
                q.push(node->right);
        }
    }

    // 寻找最小的键值
    Key minimum()
    {
        assert(count != 0);
        Node *minNode = _minimum(root);
        return minNode->key;
    }

    // 寻找最大的键值
    Key maximum()
    {
        assert(count != 0);
        Node *maxNode = _maximum(root);
        return maxNode->key;
    }

    // 从二叉搜索树中删除最小值所在节点
    void removeMin()
    {
        if (root)
            root = _removeMin(root);
    }

    // 从二叉搜索树中删除最大值所在节点
    void removeMax()
    {
        if (root)
            root = _removeMax(root);
    }

    // 从二叉搜索树中删除键值为key的节点
    void remove(Key key)
    {
        root = _remove(root, key);
    }

    bool isValidBST()
    {
        pre = nullptr;
        res = true;
        if (!root)
            return true;
        _dfs(root);
        return res;
    }

private:
    // 判断中序遍历是否有序
    void _dfs(Node *root)
    {
        if (!root)
            return;
        _dfs(root->left);

        if (pre && pre->key >= root->key)
        {
            res = false;
            return;
        }

        pre = root;
        _dfs(root->right);
    }

    // 向以node为根的二叉搜索树中,插入节点(key, value)
    // 返回插入新节点后的二叉搜索树的根
    Node *_insert(Node *node, Key key, Value value)
    {

        if (node == NULL)
        {
            count++;
            return new Node(key, value);
        }

        if (key == node->key)
            node->value = value;
        else if (key < node->key)
            node->left = _insert(node->left, key, value);
        else // key > node->key
            node->right = _insert(node->right, key, value);

        return node;
    }

    // 查看以node为根的二叉搜索树中是否包含键值为key的节点
    bool _contain(Node *node, Key key)
    {
        if (node == NULL)
            return false;

        if (key == node->key)
            return true;
        else if (key < node->key)
            return _contain(node->left, key);
        else // key > node->key
            return _contain(node->right, key);
    }

    // 在以node为根节点的二叉搜索树中查找key所对应的value
    Value *_search(Node *node, Key key)
    {
        if (node == NULL)
            return NULL;

        if (key == node->key)
            return &(node->value);
        else if (key < node->key)
            return _search(node->left, key);
        else // key > node->key
            return _search(node->right, key);
    }

    // 对以node为根的二叉搜索树进行前序遍历
    void _preOrder(Node *node)
    {
        if (node != NULL)
        {
            cout << node->key << " ";
            _preOrder(node->left);
            _preOrder(node->right);
        }
    }

    // 对以node为根的二叉搜索树进行中序遍历
    void _inOrder(Node *node)
    {
        if (node != NULL)
        {
            _inOrder(node->left);
            cout << node->key << " ";
            _inOrder(node->right);
        }
    }

    // 对以node为根的二叉搜索树进行后序遍历
    void _postOrder(Node *node)
    {
        if (node != NULL)
        {
            _postOrder(node->left);
            _postOrder(node->right);
            cout << node->key << " ";
        }
    }

    void _destroy(Node *node)
    {
        if (node != NULL)
        {
            _destroy(node->left);
            _destroy(node->right);
            delete node;
            count--;
        }
    }

    // 在以node为根的二叉搜索树中,返回最小键值的节点
    Node *_minimum(Node *node)
    {
        if (node->left == NULL)
            return node;
        return _minimum(node->left);
    }

    Node *_maximum(Node *node)
    {
        if (node->right == NULL)
            return node;
        return _maximum(node->right);
    }

    // 删除掉以node为根的二分搜索树中的最小节点
    // 返回删除节点后新的二分搜索树的根
    Node *_removeMin(Node *node)
    {
        if (node->left == NULL)
        {
            Node *rightNode = node->right;
            delete node;
            count--;
            return rightNode;
        }

        node->left = _removeMin(node->left);
        return node;
    }

    // 删除掉以node为根的二分搜索树中的最小节点
    // 返回删除节点后新的二分搜索树的根
    Node *_removeMax(Node *node)
    {
        if (node->right == NULL)
        {
            Node *leftNode = node->left;
            delete node;
            count--;
            return leftNode;
        }

        node->right = _removeMax(node->right);
        return node;
    }

    // 删除以node为根的二分搜索树中键值为key的节点
    // 返回删除节点后的新的二分搜索树的根
    Node *_remove(Node *node, Key key)
    {
        if (node == NULL)
            return NULL;
        if (key < node->key)
        {
            node->left = _remove(node->left, key);
            return node;
        }
        else if (key > node->key)
        {
            node->right = _remove(node->right, key);
            return node;
        }
        else // key == node->key
        {
            if (node->left == NULL)
            {
                Node *rightNode = node->right;
                delete node;
                count--;
                return rightNode;
            }
            if (node->right == NULL)
            {
                Node *leftNode = node->left;
                delete node;
                count--;
                return leftNode;
            }

            // node->left != NULL && node->right != NULL
            Node *successor = new Node(_minimum(node->right));
            count++;

            successor->left = node->left;
            successor->right = _removeMin(node->right);

            delete node;
            count--;

            return successor;
        }
    }
};

void judge(bool flag)
{
    cout << endl;
    if (flag)
        cout << "It is a BSTree" << endl;
    else
        cout << "It is not a BSTree" << endl;
}

int main()
{
    int q[5] = {5, 1, 4, 3, 6};
    BST<int, int> bst = BST<int, int>();
    for (int i = 0; i < 5; ++i)
    {
        bst.insert(q[i], q[i]);
    }

    judge(bst.isValidBST());
    bst.inOrder();

    bst.remove(4);
    judge(bst.isValidBST());
    bst.inOrder();

    return 0;
}

为实现红黑树准备的二叉搜索树版本(比较惊艳,嘿嘿~)

因为红黑树的增加和删除节点操作只不过是在二叉搜索树对应操作的基础上,增加了维护红黑树性质的操作嘛,所以可以在此版本基础上,实现红黑树的操作。红黑树的实现可移步到红黑树的实现(C++)_青衫客36的博客-CSDN博客

#include <iostream>
using namespace std;

//  1  结点定义:bstreeNode
template <class T>
class bstree;

template <class T>
struct _bstTreeNode
{
    friend class bstree<T>;
    T getkey()
    {
        return key;
    }

private:
    T key;
    _bstTreeNode<T> *left;
    _bstTreeNode<T> *right;
    _bstTreeNode<T> *p;
};

//  2  二叉搜索树类声明:bstree
template <class T>
class bstree
{
public:
    bstree()
    {
        root = nullptr;
    }
    void insert(T key);
    _bstTreeNode<T> *search(T key);
    void erase(T key);
    void bstDelete(_bstTreeNode<T> *z);
    void display();
    bool isValidBST();

private:
    // 用于判断是否为二叉搜索树的变量
    _bstTreeNode<T> *pre = nullptr;
    bool res = true;

    void _display(_bstTreeNode<T> *);

    _bstTreeNode<T> *treeSuccessor(_bstTreeNode<T> *);
    void _dfs(_bstTreeNode<T> *);

    _bstTreeNode<T> *root;
};

//  3  二叉搜索树类成员定义
template <class T>
void bstree<T>::insert(T key)
{
    _bstTreeNode<T> *t = new _bstTreeNode<T>; // t为新插入的节点
    t->key = key;
    _bstTreeNode<T> *x = root;
    _bstTreeNode<T> *y = nullptr;
    while (x != nullptr) // 循环结束y指向新节点应挂载的节点
    {
        y = x;
        if (key < x->key)
            x = x->left;
        else
            x = x->right;
    }
    t->p = y; // 将新插入的节点t挂在y指向节点上
    if (y == nullptr)
        root = t;
    else // 将新插入的节点t经判断后挂在y的左孩子或右孩子
    {
        if (t->key < y->key)
            y->left = t;
        else
            y->right = t;
    }
    t->left = nullptr;
    t->right = nullptr;
}

template <class T>
bool bstree<T>::isValidBST()
{
    pre = nullptr;
    res = true;
    if (!root)
        return true;
    _dfs(root);
    return res;
}

// 判断中序遍历是否有序
template <class T>
void bstree<T>::_dfs(_bstTreeNode<T> *root)
{
    if (!root)
        return;
    _dfs(root->left);

    if (pre && pre->key >= root->key)
    {
        res = false;
        return;
    }

    pre = root;
    _dfs(root->right);
}

template <class T>
_bstTreeNode<T> *bstree<T>::search(T key)
{
    _bstTreeNode<T> *x = root;
    while (x != nullptr && key != x->key)
    {
        if (key < x->key)
            x = x->left;
        else
            x = x->right;
    }
    return x;
}

template <class T>
void bstree<T>::erase(T key)
{
    _bstTreeNode<T> *x = search(key);
    if (x != nullptr)
        bstDelete(x);
}

template <class T>
_bstTreeNode<T> *bstree<T>::treeSuccessor(_bstTreeNode<T> *x)
{
    x = x->right;
    while (x->left != nullptr)
        x = x->left;
    return x;
}

template <class T>
void bstree<T>::bstDelete(_bstTreeNode<T> *z) // z是逻辑上被删除的节点(实际上他并没有被删除,而是修改了自己的value值为中序后继的值,然后把中序后继节点y删除了)
{
    _bstTreeNode<T> *x;                  // 取代被删除节点位置的节点
    _bstTreeNode<T> *y;                  // 真正删除的节点
    if (z->left == nullptr || z->right == nullptr) // z只有左子树或只有右子树,就直接把子树的根节点提上去
        y = z;
    else
        y = treeSuccessor(z); // z左右子树都有,那么就把z的中序后继提上去
    if (y->left != nullptr)   // 然后准备删除y,类似于链表的删除,将y的子节点挂在y的父节点下(取代y的位置)
        x = y->left;
    else
        x = y->right;
    
    if (x != nullptr)
        x->p = y->p;

    if (y->p == nullptr)
        root = x;
    else
    {
        if (y == y->p->left)
            y->p->left = x;
        else
            y->p->right = x; // 将y的父节点与x进行双向链接
    }
    if (y != z)          // 逻辑上删除的节点与真正删除的节点不是同一个,就把实际上删除的节点的值赋给逻辑上删除的节点,(是同一个节点的话,直接把该节点删了就可以了)
        z->key = y->key; // 将y的值赋给z使之满足二叉搜索树的性质
    delete y;            // 如果删除的节点是红色,直接删除就可以了,不需要调整红黑树
}

template <class T>
void bstree<T>::display()
{
    if (root != nullptr)
        _display(root);
    else
        cout << "Tree is empty! " << endl;
}

template <class T>
void bstree<T>::_display(_bstTreeNode<T> *x)
{
    if (x->left != nullptr)
        _display(x->left);
    if (x != nullptr)
    {
        cout << x->key << " ";
        if (x->p != nullptr)
        {
            cout << "x->p: " << x->p->key << " ";
        }
        if (x->left != nullptr)
        {
            cout << "x->left: " << x->left->key << " ";
        }
        if (x->right != nullptr)
        {
            cout << "x->right: " << x->right->key;
        }
    }
    cout << endl;
    if (x->right != nullptr)
        _display(x->right);
}

void judge(bool flag)
{
    if (flag)
        cout << "It is a BST" << endl;
    else
        cout << "It is not a BST" << endl;
}

//  4  测试程序:main()
// const int N = 12;

// int p[12] = {1, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
int p[6] = {41, 38, 31, 12, 19, 8};

int main()
{
    bstree<int> test;
    for (int i = 0; i < 6; ++i)
    {
        test.insert(p[i]);
    }
    test.display();
    judge(test.isValidBST());

    int a;
    while (cin >> a)
    {
        test.erase(a);
        // cout << test.isbstree() << endl;
        test.display();
        judge(test.isValidBST());
    }
    return 0;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

二分搜索树 的相关文章

  • MongoDB C# 驱动程序检查身份验证状态和角色

    这是我使用 MongoDB 身份验证机制登录 MongoDB 的代码 try var credential MongoCredential CreateMongoCRCredential test admin 123456 var sett
  • 高级 Win32 图像文件 I/O?

    我想在 Windows C 应用程序中将图像文件读入内存 什么是一个相当简单的解决方案 也许类似于 IOS 提供的UIImage 我希望支持合理数量的文件格式 我需要为图像处理的位图提供一些低级访问权限 我在互联网上阅读了很多内容 看起来
  • 如何自定义 DataTable 列的排序

    我需要对数据表列的值进行排序 该列包含字符串 整数或混合文本 例如 数据表列包含如下值 23 18 12 store 23 store a1 1283 25 如果我使用对值进行排序Dataview sort 方法会按此顺序产生 12 128
  • 如何知道并加载特定文件夹中的所有图像?

    我有一个应用程序 C Builder 6 0 需要知道特定文件夹中的图像总数 然后我必须加载它们 在 ImageList 或 ComboBoxEx 中 或任何其他控件中 我怎样才能做到这一点 我知道如何在控件中加载图像 或保存在 TList
  • 如何调试参数化 SQL 查询

    我使用 C 连接到数据库 然后使用 Ad hoc SQL 来获取数据 这个简单的 SQL 查询非常方便调试 因为我可以记录 SQL 查询字符串 如果我使用参数化 SQL 查询命令 有没有办法记录 sql 查询字符串以进行调试 我想就是这样的
  • 测试 hdf5/c++ 中的组是否存在

    我正在打开一个现有的 HDF5 文件来附加数据 我想向那个叫做的小组保证 A存在以供后续访问 我正在寻找一种简单的方法来创建 A有条件地 如果不存在则创建并返回新组 或者返回现有组 一种方法是测试 A存在 我怎样才能高效地做到这一点 根据
  • 防止复制构造和返回值引用的分配

    如果我有一个函数返回对类实例的引用 但我无法控制其源 比如说list
  • 捕获当前正在播放的声音

    是否可以捕获计算机上当前播放的声音 如果能够将其保存为 mp3 就好了 但我认为这样做会存在一些法律问题 所以 wav 也可以 我环顾四周 有人建议使用虚拟音频线之类的东西 在 C 中捕获声音输出 https stackoverflow c
  • 推送 Lua 表

    我已经创建了一个Lua表C 但我不知道如何将该表推入堆栈顶部 以便我可以将其传递给 Lua 函数 有谁知道如何做到这一点 这是我当前的代码 lua createtable state libraries size 0 int table i
  • C++ Primer 5th Edition 错误 bool 值没有指定最小大小?

    bool 的最小大小不应该是 1 个字节吗 这有点学术性的东西 尽管它们会转换为数字 并且 与其他所有事物一样 它们最终将基本上由计算机内存中的数字表示 但布尔值不是数字 你的bool可以取值true 或值false 即使您确实需要至少 1
  • 使用 Linq 进行异步Where过滤

    我有一个List通过填充的元素async调用 WebService 没问题 我需要过滤该列表以便在应用程序视图上显示某些内容 我试过这个 List
  • 用 C# 编写的带有点击移动的 WPF 游戏

    我试图将标签网格移动到鼠标的位置 就像冒险游戏中的移动一样 理想情况下 我会在途中删除并重新绘制它们 但是 现在我只想弄清楚如何将 int 转换为厚度或 pointtoscreen 到目前为止我有 player XMove int Mous
  • 便携式终端

    有没有办法根据所使用的操作系统自动使用正确的 EOL 字符 我在想类似的事情std eol 我知道使用预处理器指令非常容易 但很好奇它是否已经可用 我感兴趣的是 我的应用程序中通常有一些消息 稍后我会将这些消息组合成一个字符串 并且我希望将
  • C# - 为什么我需要初始化 [Out] 参数

    我有几个从本机 dll 导入的方法 使用以下语法 internal static class DllClass DllImport Example dll EntryPoint ExampleFunction public static e
  • 删除对象时指针自动指向空

    假设我有一个对象和其他几个不同类类型的对象中的 10 个指向它的指针 如果对象被删除 这些指针必须设置为空 通常我会将对象的类与具有指向它的指针的类互连 以便它可以通知它们它正在被删除 并且它们可以将它们的指针设置为空 但这也有一个负担 即
  • 宏观评价[重复]

    这个问题在这里已经有答案了 可能的重复 未定义的行为和序列点 https stackoverflow com questions 4176328 undefined behavior and sequence points 我无法理解以下宏
  • 如何使用 g++ 在 c++ 20 中使用模块?

    我读了这个链接https gcc gnu org wiki cxx modules https gcc gnu org wiki cxx modules并尝试从该网站复制以下示例 我已经知道这个编译器部分支持模块系统 注 我用的是windo
  • 从 C# 中的 .NET SecureString 读取单个字符?

    WPF 的PasswordBox 返回一个SecureString 它对窥探者隐藏密码 问题是你最终必须获得密码的值 而我在网上找到的建议都涉及将值复制到字符串中 这会让你回到窥探者的问题 IntPtr bstr Marshal Secur
  • 如何使复选框不可选择?

    我想知道你是怎么做的CheckBox在c 中无法选择 我认为这会是类似 SetSelectable false 之类的东西 但我似乎看不到该方法 I found CanSelect但这似乎是只读属性 您可以设置自动检查 http msdn
  • ASP.NET Core:会话 ID 始终变化

    今天启动了一个全新的 ASP NET Core 网站 按照说明添加会话 我们在索引页上打印出会话 ID 它始终是唯一的 我认为这可能是 cookie 合规性 所以我在 Chrome 的高级设置和调试器中删除了所有 cookie 但横幅不会再

随机推荐

  • Go语言面试题--进阶语法(33)

    文章目录 1 下面哪一行代码会 panic 请说明原因 2 下面的代码输出什么 3 下面的代码输出什么 4 下面哪一行代码会 panic 请说明原因 1 下面哪一行代码会 panic 请说明原因 package main func main
  • 无监督学习分类

    无监督学习的核心思想是构建出一个与待测样本最相近的 模板 与之比较 根据像素或特征的差异性实现缺陷得到检出与定位 根据维度不同 分为两种方法 1 基于图像相似度的方法 该方法在图像像素层面进行比较 核心思想是重建出与输入样本最相近的正常图像
  • 13.s日志查询

    mysql慢查询 慢查询日志是MySQL提供的一种日志记录 它用来记录在MySQL中相应时间超过时间阈值的语句 具体指运行时间超过long query time值的SQL 则会被记录到慢查询日志中 具体指运行时间超过long query t
  • OTT不允许做电视频道直播,但活动直播并未有文件禁止

    根据广电总局文件 OTT不允许做直播 但看到各种体育直播 活动直播在OTT盒子上却很多 一直以来 一直纳闷活动直播是否应该归为直播 今天听了下OTT牌照商的朋友 关于这个问题的看法 电视频道直播目前在OTT上是绝不允许做的 这个总局有文件规
  • Chisel3-创建工程并转换为Verilog代码

    https mp weixin qq com s ie0R3v60IcrI6beTXHrgSg 基于Intellj IDEA Scala插件模式开发 因为Chisel内嵌于Scala 所以Chisel3的项目实际上是Scala的项目 构建使
  • keil5中如何配置ST-LINK下载

    首先打开keil5软件 打开之后鼠标点击小锤子的标志 打开之后选择Debug 进入Settings后 我们选择这几项 上图进行这两步之后 再点击 FlashDownload 然后点击确定返回第一次打开的界面 最后点击确定 到此ST Link
  • 逻辑电平(TTL/CMOS/LVDS/LVPECL/CML)

    低速逻辑电平 TTL CMOS LVTTL LVCMOS逻辑电平介绍 传统单板设计中 TTL和CMOS逻辑电平被广泛应用 是数字电路设计中最常见的两种逻辑电平 LVTTL和LVCMOS是它们的低电平版本 TTL Transistor Tra
  • 全局监控 click 点击事件的四种方式

    本文主要给大家分享如何在全局上去监听 click 点击事件 并做些通用处理或是拦截 使用场景可能就是具体的全局防快速重复点击 或是通用打点分析上报 用户行为监控等 以下将以四种不同的思路和实现方式去监控全局的点击操作 由简单到复杂逐一讲解
  • Linux下xargs命令详解

    Linux下xargs命令详解 1 简介 之所以能用到这个命令 关键是由于很多命令不支持 管道来传递参数 而日常工作中有有这个必要 所以就有了xargs命令 例如 find sbin perm 700 ls l 这个命令是错误的 find
  • STM32F407+ESP8266连接机智云过程详解

    工程创建 代码调试过程参见 STM32F407 ESP8266 程序源码下载 STM32F407 ESP8266连接机智云程序源码
  • TCP实现服务器端接收客户端发送的数据

    一 服务器端接收 include
  • 面试官:Java 内存泄漏了,怎么排查?

    您好 我是路人 更多优质文章见个人博客 http itsoku com 由来 前些日子小组内安排值班 轮流看顾我们的服务 主要做一些报警邮件处理 Bug 排查 运营 issue 处理的事 工作日还好 无论干什么都要上班的 若是轮到周末 那这
  • 云原生时代消息中间件的演进路线

    引言 本文以一张云进化历史图开场 来谈谈云原生时代消息中间件的演进路线 但本文绝对不是 开局一张图 内容全靠编 从虚拟化技术诞生以来 IaaS PaaS SaaS 概念陆续被提了出来 各种容器技术层出不穷 到 2015 年 Cloud Na
  • 微信小程序使用腾讯位置服务路线规划插件

    微信小程序使用腾讯位置服务路线规划插件 避坑指南 在微信公众平台的设置里的第三方设置中 添加插件 然后点开详情 再点击开发文档 里面有详细的使用教程 我简单说一下 容易入的坑 首先第一点 插件不需要配置wxml啥的 直接app json引入
  • 为啥国人偏爱Mybatis,而老外喜欢Hibernate/JPA呢?

    关于SQL和ORM的争论 永远都不会终止 我也一直在思考这个问题 昨天又跟群里的小伙伴进行了一番讨论 感触还是有一些 于是就有了今天这篇文 声明 本文不会下关于Mybatis和JPA两个持久层框架哪个更好这样的结论 只是摆事实 讲道理 所以
  • [人工智能-深度学习-43]:输入预处理 - 规范化Normalization、标准化Standardization、正态分布、算术平均、方差

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 121215445 目录 第1章 多维数
  • opencv之霍夫曼变换

    霍夫变换不仅可以找出图片中的直线 也可以找出圆 椭圆 三角形等等 只要你能定义出直线方程 圆形的方程等等 不得不说 现在网上的各种博客质量真的不行 网上一堆文章 乱TM瞎写 误人子弟 本身自己就没有理解的很清楚 又不去读算法实现的源码 写的
  • alibaba Sentinel 限流

    文章目录 pom yml app controller 过滤器限流 服务限流 pom
  • 过滤多余空格

    问题描述 一个句子中也许有多个连续空格 过滤掉多余的空格 只留下一个空格 输入格式 一行 一个字符串 长度不超过 200200 句子的头和尾都没有空格 输出格式 过滤之后的句子 Sample 1 Inputcopy Outputcopy H
  • 二分搜索树

    经典写法 内心os 就是有点繁琐hh include