【C++进阶】二叉搜索树递归与非递归的模拟实现(附源码)

2023-11-12

一.什么是二叉搜索树

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

 根据二叉搜索树的性质,它的中序遍历结果就是一个升序列


二.二叉搜索树的模拟实现

节点 Node

在实现二叉搜索树之前,要先定义一个节点,成员变量包括左指针(left),右指针(right)和一个值 (key)

template<class K>
struct BSTNode
{
	BSTNode<K>* _left;
	BSTNode<K>* _right;
	K _key;

	BSTNode(const K& key)  //构造函数
		:_left(nullptr)
		,_right(nullptr)
		,_key(key)
	{}
};

打印 InOrder

打印二叉搜索树树,就是对树进行中序遍历

这里有个小技巧,可以不用写 Getroot 函数就可以使用到类里面的成员变量:

        就是在函数里再套一个函数

具体看代码实现:

void InOrder()   //函数里面再套一个函数,真正实现中序遍历的是 _InOrder函数
{
	_InOrder(_root);
	cout << endl;
}

void _InOrder(Node* root)
{
	if (root == nullptr)
		return;

	_InOrder(root->_left);
	cout << root->_key << " ";
	_InOrder(root->_right);
}

插入 insert

在插入时,如果要插入的数据(key)已经存在,则返回false,若不存在,则完成插入操作,然后返回true

其实这刚好也完成了去重的操作。

插入操作:

        定义一个parent指针,记录当前节点的父节点,方便最后插入节点时的链接

        定义一个cur指针,用于遍历整个树。

根据二叉搜素树的性质:

        当key小于节点的 _key 时,更新parent,就到树的左边

        当key大于节点的 _key 时,更新parent,就到树的右边

        当cur为空时,跳出循环,进行节点间的链接操作(同样遵循二叉搜索树的性质)

bool Insert(const K& key)
{
	if (_root == nullptr)  //注意树为空的情况
	{
		_root = new Node(key);
		return true;
	}

	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key < key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_key > key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
			return false;
	}

	cur = new Node(key);   
	if (parent->_key<key)   //链接
	{
		parent->_right = cur;
	}
	else
		parent->_left = cur;

	return true;

}

插入的递归实现  insertR

既然要递归,那么肯定要用到根节点,同样使用中序遍历那样的方式,函数里再套一个函数

其实理论还是和非递归的一样,只不过换成了调用函数,但这里有个小窍门,就是我们可以传根节点的引用,这样就不用定义一个父节点指针了,根据引用的特性,引用是一个变量的别名,当我们递归到下一层时,此时传过来的root就是这一层的父节点,直接链接就行了。

bool insertR(const K& key)
{
	return _insertR(_root, key);
}

bool _insertR(Node*& root, const K& key)
{
	if (root == nullptr)
	{
		Node* newroot = new Node(key);
		root = newroot;
		return true;
	}

	if (root->_key < key)
	{
		return _insertR(root->_right, key);
	}
	else if (root->_key > key)
	{
		return _insertR(root->_left, key);
	}
	else
		return false;
}

删除 erase

删除就有点复杂了,它分几种情况:

首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情
况:

        1.要删除的结点无孩子结点
        2. 要删除的结点只有左孩子结点
        3. 要删除的结点只有右孩子结点
        4. 要删除的结点有左、右孩子结点

前三种情况倒好解决,如果待删除的节点只有一个孩子,那么只需要把这个孩子根据二叉搜索树的性质托孤给它的父节点。

很显然,当有两个孩子时,托孤就行不通了,那么我们可不可以重新找一个符合条件的节点来充当父节点 ?

答案当然是可以,这就是替换法

替换法:

        找左子树的最大节点(在最右边),或是右子树的最小节点(在最左边)

代码实现:

bool erase(const K& key)
{
	Node* cur = _root;
	Node* parent = nullptr;
	while (cur)   //先查找待删除节点,没找到则返回false
	{
		if (cur->_key < key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_key > key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else   //找到了
		{
			if (cur->_left == nullptr)  //有一个孩子,且左子树为空
			{
				if (parent == nullptr)   //如果待删除节点是根节点,那么parent为空
				{
					_root = cur->_right;
					return true;
				}

			    if (parent->_left == cur)
				{
					parent->_left = cur->_right;
				}
				else
					parent->_right = cur->_right;

			}
			else if (cur->_right == nullptr)  //有一个孩子,且右子树为空
			{
				if (parent == nullptr)     //如果待删除节点是根节点,那么parent为空
				{
					_root = cur->_left;
				}

				if (parent->_left == cur)
				{
					parent->_left = cur->_left;
				}
				else
					parent->_right = cur->_left;
		
				}
			else //有两个孩子,替换法,这里是寻找左子树的最大节点
			{
				Node* parent = cur;
				Node* leftmax = cur->_left;
				while (leftmax->_right)   //找左子树最大的
				{
					parent = leftmax;
					leftmax = leftmax->_right;
				}

				std::swap(leftmax->_key, cur->_key);  //交换值
				if (parent->_left == leftmax)
				{
					parent->_left = leftmax->_left;
				}
				else
					parent->_right = leftmax->_left;

				cur = leftmax;
			}

				delete cur;  //删除节点
				return true;
		}

	}

			return false;   //没找到返回false
}

删除的递归实现 eraseR

同样使用函数套函数的方式。同样传root的引用

当有一个孩子或没有孩子的时候,可以直接链接,然后再删除;

当有两个孩子的时候,同样使用替换法,找到左子树的最大节点(或是右子树的最小节点),此时这个最大节点(或是最小节点)一定没有孩子,再递归一次,转换成没有孩子的情况

bool eraseR(const K& key)
{
	return _eraseR(_root, key);
}

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  //找到了待删除节点
	{
		Node* del = root;
		if (root->_left == nullptr)   //有一个叶节点或没有叶节点
		{
			root = root->_right;
		}
		else if (root->_right == nullptr)
		{
			root = root->_left;
		}
		else   //有两个叶节点
		{
			Node* leftmax = root->_left;
			while (leftmax->_right)
			{
				leftmax = leftmax->_right;
			}

			std::swap(leftmax->_key, root->_key); 

			return _eraseR(root->_left, key);  //注意这里要传root->left,不能传leftmax,否                
                                               //则链接关系会对不上
		}
		delete del;   //删除节点
		return true;
	}
}

剩下接口

剩下的操作(查找find ,拷贝构造,重载赋值运算符,析构函数)都很简单了,就不一一讲述,都放在源码里了。


三.源码

Binary_Search_Tree.h

template<class K>
struct BSTNode
{
	BSTNode<K>* _left;
	BSTNode<K>* _right;
	K _key;

	BSTNode(const K& key)  //构造函数
		:_left(nullptr)
		,_right(nullptr)
		,_key(key)
	{}
};

template<class K>
class BSTree
{
    typedef BSTNode<K> Node;
public:
    BSTree()  //构造函数
		:_root(nullptr)
	{}
    void InOrder()   //函数里面再套一个函数,真正实现中序遍历的是 _InOrder函数
    {
	    _InOrder(_root);
	    cout << endl;
    }
    bool Insert(const K& key)
    {
	    if (_root == nullptr)  //注意树为空的情况
	    {
		    _root = new Node(key);
		    return true;
	    }

	    Node* parent = nullptr;
	    Node* cur = _root;
	    while (cur)
	    {
		    if (cur->_key < key)
		    {
			    parent = cur;
			    cur = cur->_right;
		    }
		    else if (cur->_key > key)
		    {
			    parent = cur;
			    cur = cur->_left;
		    }
		    else
			    return false;
	    }

	    cur = new Node(key);   
	    if (parent->_key<key)   //链接
	    {
		    parent->_right = cur;
	    }
	    else
		    parent->_left = cur;

	    return true;

    }

    bool insertR(const K& key)   //插入的递归实现
    {
	    return _insertR(_root, key);
    }
    
    bool erase(const K& key)
    {
	    Node* cur = _root;
	    Node* parent = nullptr;
	    while (cur)   //先查找待删除节点,没找到则返回false
	    {
		    if (cur->_key < key)
		    {
		    	parent = cur;
		    	cur = cur->_right;
		    }
		    else if (cur->_key > key)
	    	{
		    	parent = cur;
	       		cur = cur->_left;
		    }
		    else   //找到了
		    {
		       	if (cur->_left == nullptr)  //有一个孩子,且左子树为空
			    {
			    	if (parent == nullptr)   //如果待删除节点是根节点,那么parent为空
			    	{
				    	_root = cur->_right;
				    	return true;
			    	}

			        if (parent->_left == cur)
			    	{
			       		parent->_left = cur->_right;
			    	}
			    	else
					    parent->_right = cur->_right;

			    }
			    else if (cur->_right == nullptr)  //有一个孩子,且右子树为空
			    {
			    	if (parent == nullptr)     //如果待删除节点是根节点,那么parent为空
			    	{
			    		_root = cur->_left;
			    	}

				    if (parent->_left == cur)
				    {
				    	parent->_left = cur->_left;
			    	}
				    else
				    	parent->_right = cur->_left;
		
				}
			    else //有两个孩子,替换法,这里是寻找左子树的最大节点
			    {
			    	Node* parent = cur;
			    	Node* leftmax = cur->_left;
			    	while (leftmax->_right)   //找左子树最大的
			    	{
			    		parent = leftmax;
			    		leftmax = leftmax->_right;
			    	}

			    	std::swap(leftmax->_key, cur->_key);  //交换值
			    	if (parent->_left == leftmax)
			    	{
			    		parent->_left = leftmax->_left;
				    }
			    	else
				    	parent->_right = leftmax->_left;

			    	cur = leftmax;
			    }

			    	delete cur;  //删除节点
				    return true;
		    }

	    }
			return false;   //没找到返回false
    }
    
    bool eraseR(const K& key)   //删除的递归实现
    {
    	return _eraseR(_root, key);
    }

    bool find(const K& key)   //查找
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else
				return true;
		}

		return false;
	}

    bool findR(const K& key)   //查找的递归实现
	{
		return _findR(_root, key);
	}
    ~BSTree()   //析构函数
	{
		destroy(_root);
	}

	BSTree(const BSTree<K>& bst)  //拷贝构造
	{
		_root = copy(bst._root);
	}

	BSTree<K>& operator=(BSTree<K> bst)  //赋值重载
	{
		std::swap(_root, bst._root);

		return *this;
	}
private:
    void _InOrder(Node* root)
    {
	    if (root == nullptr)
	    	return;

	    _InOrder(root->_left);
	    cout << root->_key << " ";
	    _InOrder(root->_right);
    }
    
    bool _insertR(Node*& root, const K& key)
    {
	    if (root == nullptr)
	    {
		    Node* newroot = new Node(key);
		    root = newroot;
		    return true;
	    }

	    if (root->_key < key)
	    {
		    return _insertR(root->_right, key);
	    }
	    else if (root->_key > key)
	    {
		    return _insertR(root->_left, key);
	    }
	    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  //找到了待删除节点
	    {
		    Node* del = root;
		    if (root->_left == nullptr)   //有一个叶节点或没有叶节点
		    {
			    root = root->_right;
	    	}
		    else if (root->_right == nullptr)
		    {
			    root = root->_left;
	    	}
		    else   //有两个叶节点
		    {
			    Node* leftmax = root->_left;
			    while (leftmax->_right)
			    {
				    leftmax = leftmax->_right;
			    }

			    std::swap(leftmax->_key, root->_key); 

			 return _eraseR(root->_left, key);  //注意这里要传root->left,不能传leftmax,否                
                                               //则链接关系会对不上
		    }
		    delete del;   //删除节点
		    return true;
	    }
    }

    Node* copy(Node* root)
	{
		if (root == nullptr)
			return nullptr;
		Node* copyroot = new Node(root->_key);
		copyroot->_left = copy(root->_left);
		copyroot->_right = copy(root->_right);
		return copyroot;
	}
	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;
	}
    
    void destroy(Node* root)  //后序删除
	{
		if (root == nullptr)
			return;
		destroy(root->_left);
		destroy(root->_right);
		delete root;
		root = nullptr;
	}
private:
    Node* _root;
};

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

【C++进阶】二叉搜索树递归与非递归的模拟实现(附源码) 的相关文章

  • 从 Invoke 方法获取 RETURN

    我正在尝试从另一个线程上的列表框项目中读取值 我尝试创建一种新方法来运行调用命令 我可以设法将命令发送到列表框 例如通过调用方法添加 但我似乎无法得到响应 我似乎无法获取该项目的值 我尝试了几种方法 一旦我将它从空变为字符串 事情就开始变得
  • 从另一个 FORM 中取回隐藏的 FORM

    我有两种形式Form1 and Form2 我正在打开Form2 from Form1 on button Click Form2 obj2 new Form2 this Visible false obj2 Show 然后我想回来Form
  • 使用 Xamarin.Forms 和 Zxing 生成 QR 码

    我在网上看到了很多关于这个的内容 旧帖子 但似乎没有什么对我有用 我正在尝试从字符串中生成二维码并将其显示在应用程序中 这就是我一开始的情况 qrCode new ZXingBarcodeImageView BarcodeFormat Ba
  • 为什么在 C++ 中声明枚举时使用 typedef?

    我已经很多年没有写过任何 C 了 现在我正试图重新开始 然后我遇到了这个并考虑放弃 typedef enum TokenType blah1 0x00000000 blah2 0X01000000 blah3 0X02000000 Toke
  • C# Outlook 从收件人获取 CompanyName 属性

    我目前正在使用 C 编写 Outlook 2010 AddIn 我想要的是从我从 AppointmentItem 中提取的 Recipient 对象中获取 CompanyName 属性 因此 有了 AppointmentItem 的收件人
  • 具有多个谓词的 C++11 算法

    功能如std find if来自algorithmheader 确实很有用 但对我来说 一个严重的限制是我只能为每次调用使用 1 个谓词count if 例如给定一个像这样的容器std vector我想同时应用相同的迭代find if 多个
  • DataGridView 列中的数字文本框

    我有一个DataGridView 我想要它的第一列或任何所需的列 其中有textboxes在其中 成为NUMERIC ONLY 我目前正在使用这段代码 private void dataGridViewItems EditingContro
  • 为什么 C# 中同一类型的隐式和显式运算符不能共存? [复制]

    这个问题在这里已经有答案了 为什么同一类中两个相同类型的运算符 显式和隐式 不能共存 假设我有以下内容 public class Fahrenheit public float Degrees get set public Fahrenhe
  • 如何调试在发布版本中优化的变量

    我用的是VS2010 我的调试版本工作正常 但我的发布版本不断崩溃 因此 在发布版本模式下 我右键单击该项目 选择 调试 然后选择 启动新实例 此时我看到我声明的一个数组 int ma 4 1 2 8 4 永远不会被初始化 关于可能发生的事
  • 虚拟并行端口模拟器

    在我的计算机网络课程中 我们应该通过使用本机寄存器 例如使用 outportb 等命令 来学习并行端口编程 我没有并行端口 因为我住在 2011 年 但想练习这些程序 我使用 dosbox 安装了旧的 Turboc 3 IDE 有没有一个程
  • 关闭整数的最右边设置位

    我只需要关闭最右边的设置位即可 我的方法是找到最右边位的位置 然后离开该位 我编写这段代码是为了这样做 int POS int n int p 0 while n if n 2 0 p else break n n 2 return p i
  • 提升mapped_file_source、对齐方式和页面大小

    我正在尝试在性能很重要的上下文中解析一些大小高达几百兆字节的文本文件 因此我使用 boostmapped file source 解析器期望源以空字节终止 因此我想检查文件大小是否是页面大小的精确倍数 如果是 则使用较慢的非内存映射方法 我
  • 检测 TextBox 中的 Tab 键按下

    I am trying to detect the Tab key press in a TextBox I know that the Tab key does not trigger the KeyDown KeyUp or the K
  • 为什么 std::function 不是有效的模板参数,而函数指针却是?

    我已经定义了名为的类模板CallBackAtInit其唯一目的是在初始化时调用函数 构造函数 该函数在模板参数中指定 问题是模板不接受std function作为参数 但它们接受函数指针 为什么 这是我的代码 include
  • 如何增加ofstream的缓冲区大小

    我想增加 C 程序的缓冲区大小 以便它不会过于频繁地写入 默认缓冲区是 8192 字节 我尝试使用 pubsetbuf 将其增加到 200K 原始代码 ofstream fq fastq1 cstr ios out fastq1 is a
  • 在 C++ 代码 gdb 中回溯指针

    我在运行 C 应用程序时遇到段错误 在 gdb 中 它显示我的一个指针位置已损坏 但我在应用程序期间创建了 10 万个这样的对象指针 我怎样才能看到导致崩溃的一个 我可以在 bt 命令中执行任何操作来查看该指针的生命周期吗 谢谢 鲁奇 据我
  • WPF DataGrid - 在每行末尾添加按钮

    我想在数据网格的每一行的末尾添加一个按钮 我找到了以下 xaml 但它将按钮添加到开头 有人知道如何在所有数据绑定列之后添加它吗 这会将按钮添加到开头而不是末尾
  • 时间:2019-03-17 标签:c#TimerStopConfusion

    我想通过单击按钮时更改文本颜色来将文本框文本设置为 闪烁 我可以让文本按照我想要的方式闪烁 但我希望它在闪烁几次后停止 我不知道如何在计时器触发几次后让它停止 这是我的代码 public Form1 InitializeComponent
  • Unity,c++ 本机插件字节数组不匹配

    在我的 C 本机插件中 我有一个调用 vector
  • IDisposable 的显式实现

    虽然有很多关于IDisposable在 SO 上找到 我还没有找到答案 我通常遵循这样的做法 当我的一个班级拥有一个IDisposable对象然后它也实现IDisposable并打电话Dispose在拥有的对象上 然而最近我遇到了一个类 它

随机推荐

  • 刷脸支付以人脸为密码的支付方式蔚然成风

    在移动支付高速发展的现在 人们的每一次支付行为都与移动支付息息相关 尤其在中国 移动支付的使用率已经远远超出现金支付 人们对现金不再产生依赖 刷脸支付诞生后 人们对手机也不再产生依赖 因为出行仅靠刷脸即可 我国的移动支付水平在全国都处于领先
  • Springboot 多模块集成mybatis提示:Invalid bound statement (not found)

    1 第一步 检查提示错误信息接口namespace 文件是否对应 MyBatis 文件Mapper 接口定义与Mapper xml 文件定义一致 2 整体项目结构截图如下 从项目结构来看 包含两个子模块包含MyBatis 的mapper 文
  • Element之el-switch上显示文字

    如果我们想要在Element组件库中的switch开关组件上显示文字该怎么做 1 Html部分
  • pclint html报告,PC-lint 9 + 中文手册

    实例简介 PCLINT是一种代码检查工具 还包括中文文档 实例截图 核心代码 Pc Lint9 pclint autorun inf DOS ins choose16 exe choose exe install exe lint exe
  • ACE_Message_Block例子

    include ace OS h include ace Message Block h include ace FILE IO h include
  • 【Absible学习】Ansible常用模块---包管理模块

    yum repository模块 yum repository模块可以管理远程主机上的yum仓库 模块参数 参数 说明 name 必须参数 用于指定要操作的唯一的仓库ID 也就是 repo 配置文件中每个仓库对应的 中括号 内的仓库ID b
  • 深度学习--手写数字识别<一>

    手写字符识别数据集THE MNIST DATABASE of handwritten digits http yann lecun com exdb mnist 其中训练集60000例 测试集10000例 加载数据 1 读取数据 usr b
  • 博客营销分析:博客营销的优势+方法技巧+成功案例介绍

    博客营销分析 博客营销的优势 方法技巧 成功案例介绍 一 博客营销是什么 博客营销的定义 首先 先为大家介绍博客的定义 博客是一个可以发布文字 链接 图片 视频的网站 是实现信息分享与交流的平台 而博客营销是博主通过博客内容向网站用户传递信
  • [整理]MySQL8-安装、启动、卸载、初始密码、忘记密码(CentOS,Ubuntu,Widows)

    系统和MySQL更新换代都很快 我用到时会顺手整理在这里 内容没什么技术含量 也不保障内容的全和新 每次遇到问题网上搜索一下很容易找到解决方法的 我工作电脑用Widows Ubuntu WSL 服务器用CentOS 所以三个系统都安装过 C
  • 【Python】字典内容写入json文件

    Python中有序字典和无序字典 一键多值字典 Python将字典内容写入json文件 1 无序字典 目前了解三种 在Python中直接默认的是无序字典 这种不会按照你插入的顺序排序 即使你对字典排序后 返回的也是一个list变量 而不是字
  • echarts的图表双击事件、单击某块、滚轮放大缩小

    echarts的图表双击事件 dblclick
  • 华为机试:HJ2 计算某字符出现次数

    描述 写出一个程序 接受一个由字母 数字和空格组成的字符串 和一个字符 然后输出输入字符串中该字符的出现次数 不区分大小写字母 数据范围 1 le n le 1000 1 n 1000 输入描述 第一行输入一个由字母和数字以及空格组成的字符
  • Android 源码分析 - 系统 - Settings

    core 源代码 core java android provider Settings java 定义一系列配置项名称 索引 常量 SettingsProvider apk 源代码位于 frameworks base packages S
  • 【Vuex入门】——(四)Mutation

    一 Mutation的作用 更改 Vuex 的store 中的状态的唯一方法是提交 mutation Vuex 中的 mutation 非常类似于事件 每个 mutation 都有一个字符串的 事件类型 type 和 一个 回调函数 han
  • Python语言程序设计 习题6

    一 选择题 1 下列Python数据中其元素可以改变的是 A A 列表 B 元组 C 字符串 D 数组 2 表达式 2 in 1 2 3 4 的值是 D A Yes B No C True D False print 2 in 1 2 3
  • python是什么意思中文、好学吗-零基础学python难吗?好学吗?

    Python是一种什么语言 Python是一种计算机程序设计语言 你可能已经听说过很多种流行的编程语言 比如非常难学的C语言 非常流行的Java语言 适合初学者的Basic语言 适合网页编程的JavaScript语言等 Python是他们其
  • 如何使用下标遍历二维数组

    点击打开链接 int my 2d array 10 10 假定数组my 2d array 已经预先被填充了数据 int i j 遍历这个数组 for i 0 i lt 10 i 向下遍历各行 for j 0 j lt 10 j 穿越各列 p
  • Python:Unused import statement 解决方法

    Python 学习 21052501 1 Unused import statement 解决方法 Pycharm file 菜单下有Invalidate caches Restart菜单栏 点击清除缓存重新启动Pycharm即可 2 In
  • 反思深度学习与传统计算机视觉的关系

    来源 算法与数学之美 某种程度上 深度学习最大的优势就是自动创建没有人会想到的特性能力 如今 深度学习在众多领域都有一席之地 尤其是在计算机视觉领域 尽管许多人都为之深深着迷 然而 深网就相当于一个黑盒子 我们大多数人 甚至是该领域接受过培
  • 【C++进阶】二叉搜索树递归与非递归的模拟实现(附源码)

    一 什么是二叉搜索树 二叉搜索树又称二叉排序树 它或者是一棵空树 或者是具有以下性质的二叉树 根据二叉搜索树的性质 它的中序遍历结果就是一个升序列 二 二叉搜索树的模拟实现 节点 Node 在实现二叉搜索树之前 要先定义一个节点 成员变量包
Powered by Hwhale