【C++】:想知道如何实现互译字典吗?来看二叉搜索树

这篇具有很好参考价值的文章主要介绍了【C++】:想知道如何实现互译字典吗?来看二叉搜索树。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

二叉搜索树好文!

文章目录

  • 前言
  • 一、实现搜索二叉树
  • 二、二叉搜索树的应用
    • 1.K模型
    • 2.KV模型
  • 总结

前言

二叉搜索树概念

二叉搜索树又称二叉排序树,它或者是一棵空树 ,或者是具有以下性质的二叉树 :
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树
二叉搜索树也被叫做二叉排序树或二插查找树。
二叉搜索树:一颗二叉树,可以为空,如果不为空,则满足一下性质:
1.非空左子树的所有键值小于其根节点的键值
2.非空右子树的所有键值大于其根节点的键值
3.左,右子树都是二叉搜索树
如下图所示:
【C++】:想知道如何实现互译字典吗?来看二叉搜索树

一、实现搜索二叉树

我们首先要构建搜索二叉树的框架,先用一个命名空间将我们要写的二叉树放进去,我们需要用struct结构体实现一个节点:

template <class K>
	struct TreeNode
	{
		TreeNode<K>* left;
		TreeNode<K>* right;
		K _key;
		TreeNode(const K& key)
			:left(nullptr)
			, right(nullptr)
			, _key(key)
		{

		}
	};

 一个数的节点分为左子树和右子树和节点值,节点值用模板变量使用的时候就可以随意存储任意变量的节点了。同时我们要实现节点的构造函数,构造函数体内不需要实现,只需要在初始化列表把需要初始化的变量初始化即可,当然我们也可以给变量一个缺省参数:

template <class K>
	struct TreeNode
	{
		TreeNode<K>* left;
		TreeNode<K>* right;
		K _key;
		TreeNode(const K& key = K())
			:left(nullptr)
			, right(nullptr)
			, _key(key)
		{

		}
	};

 然后我们实现搜索二叉树的主体,其实很简单我们只需要有一个私有成员变量根节点即可,当然为了使用节点方便我们将节点名重命名一下:

template <class K>
	class BSTree
	{
		typedef TreeNode<K> Node;
	public:

	private:
		Node* _root = nullptr;
	};

 明白了二叉搜索树的概念我们就实现一个二插搜索树,首先我们实现插入功能,因为二叉搜索树的规律是如果插入的节点比当前节点小我就去当前节点的左树寻找,如果插入的节点比当前节点大我就去当前节点的右树查找,直到找到空指针我就将要插入的节点放在此位置,下面我们直接写代码:

bool insert(const K& val)
		{
			if (_root == nullptr)
			{
				_root = new Node(val);
				return true;
			}
			Node* cur = _root;
			Node* parent = nullptr;
			while (cur)
			{
				if (cur->_key < val)
				{
					parent = cur;
					cur = cur->right;
				}
				else if (cur->_key > val)
				{
					parent = cur;
					cur = cur->left;
				}
				else
				{
					return false;
				}
			}
			if (parent->_key < val)
			{
				parent->right = new Node(val);
			}
			else
			{
				parent->left = new Node(val);
			}
			return true;
		}

首先我们要判断这棵树是否是一颗空树,对一颗空树插入节点只需要给根节点new一个节点即可。当然要记得插入后要返回true,因为我们一次只插入一个节点。接下来我们用一个节点来遍历,遇到插入节点大于当前节点就往当前节点的右子树走,否则就往左子树走,如果发现要插入的节点在数中已经存在那么我们就不进行插入了直接返回false即可。当我们用于遍历的那个节点为空时说明找到要插入的位置了,但是这个时候节点已经从循环中出来了并且为空我们是不能直接连接的,我们需要一个节点来记录遍历节点的前一个位置,然后当遍历节点为空找到合适的位置我们还不能直接插入,因为我们不知道要插入的是在父节点的左子树还是右子树,如下图:

【C++】:想知道如何实现互译字典吗?来看二叉搜索树

 所以我们在循环完后要先判断要插入的节点是在父节点的左子树还是右子树,然后我们在进行插入,插入成功后返回true即可。

第二个功能我们实现一个中序遍历,这样就可以先测试我们写的插入功能对不对。
 


void Inorder()
		{
			_Inorder(_root);
		}

void _Inorder(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			_Inorder(root->left);
			cout << root->_key << " ";
			_Inorder(root->right);
		}

这里我们为什么要用一个函数调用另一个函数呢?因为我们要遍历一棵树是需要根节点的,但是我们要让用户调用的时候应该用对象调用,不再传参了如下图:

【C++】:想知道如何实现互译字典吗?来看二叉搜索树

 所以我们需要一个无参的函数封装,中序遍历很简单,遇到空树就返回,然后先遍历左子树打印其节点值再遍历右子树,然后在封装的函数中传入根节点即可。

3.删除节点

对于搜索二叉树来讲,删除节点应该是最复杂的,因为要分多种情况,我们一个一个来看:

第一种情况:删除叶子节点

【C++】:想知道如何实现互译字典吗?来看二叉搜索树

要删除上图中的叶子节点只需要通过搜索二叉树的规律找到其节点,然后将其释放掉,同时要让删除节点的父节点的某个子树为空。

第二种情况:

【C++】:想知道如何实现互译字典吗?来看二叉搜索树

如果要删像6和14这样的节点该怎么办呢?这样的节点的规律是只有一个子树,所以我们需要将节点删除后将他们的子树托付给他的父节点,比如删除6就需要将7托付给3的右子树,比如说要删14就要将14的左子树托付给10的右子树,因为我们不能确定要删除的节点有左子树还是右子树,并且也不能确定他是他的父节点的左右哪一颗子树,所以需要我们单独判断。

第三种情况:

【C++】:想知道如何实现互译字典吗?来看二叉搜索树

第三种情况是最复杂的,要删除的节点既有左子树,又有右子树,在这种情况下我们就需要找一个合适的节点来代替要删除的节点的位置,而了解二叉搜索树的规律后我们发现,这个合适的节点只有两个,一个是要删除节点的左子树中的最大值,第二个是要删除节点的右子树中的最小值。比如上图中要删除8可以用左树中的7替换,也可以用右子树中的10替换。再比如要删除3,那就用3的左子树的1替换或者右树中的4替换,而简单的替换也是不行的,我们发现要帮被删除节点带孩子的节点也有可能有自己的孩子,比如说8如果用10来替换,10也有自己的14这个孩子,所以这个时候我们还需要将10的孩子托付给被替换后的节点如下图:

【C++】:想知道如何实现互译字典吗?来看二叉搜索树 了解了以上几点我们就可以写代码了:

bool erase(const K& val)
	{
		if (_root == nullptr)
		{
			return false;
		}
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			if (cur->_key < val)
			{
				parent = cur;
				cur = cur->right;
			}
			else if (cur->_key > val)
			{
				parent = cur;
				cur = cur->left;
			}
			else
			{
				//找到了,有三种情况,1.要删的是叶子节点 2.要删的节点有一个子树  
                //3.要删的节点有2个子树  叶子节点和要删除的节点有一个子树可以一起判断
				//叶子节点
				//要删除的节点只有一个子树
				if (cur->left == nullptr || cur->right == nullptr)
				{
					if (cur->left)
					{
						//要防止歪脖子数,歪脖子树要删除根节点parent为空
						if (parent == nullptr)
						{
							_root = cur->left;
						}
						else if (parent->left == cur)
						{
							parent->left = cur->left;
						}
						else
						{
							parent->right = cur->left;
						}
					}
					else
					{
						if (parent == nullptr)
						{
							_root = cur->right;
						}
						else if (parent->left == cur)
						{
							parent->left = cur->right;
						}
						else
						{
							parent->right = cur->right;
						}
					}
					delete cur;
					return true;
				}
				else
				{
					//要删除的节点有两个子树,这个时候需要左子树中的最大值或者右子树中的最小 
                    值来托管
					//假如让右子树的最小值托管,右子树的最小值一定没有左子树但是可能有右子 
                    //树,所以需要记录右子树最小值的父亲来托管右子树最小值的右子树
					Node* prevparent = cur;
					Node* tmp = cur->right;
					while (tmp->left)
					{
						prevparent = tmp;
						tmp = tmp->left;
					}
					if (prevparent->left == tmp)
					{
						prevparent->left = tmp->right;
					}
					else
					{
						prevparent->right = tmp->right;
					}
					cur->_key = tmp->_key;
					delete tmp;
					return true;
				}
			}
		}
		return false;
	}

 首先如果这棵树本来就为空那么是无法删除的,返回false即可,然后我们用一个节点遍历二叉树,同时还要用一个节点记录遍历节点走之前的前一个位置也就是找父节点,先去找要删除的节点,找到后有三种情况但是我们发现要删除的节点只有一个节点和叶子节点刚好可以一起判断,所以当左树为空或者右树为空的时候就进循环,进入循环后还要判断要删除的节点到底是有左子树还是有右子树,然后再判断要删除节点是其父节点的左右哪颗子树,只有知道了这个我们才能将要删除节点的孩子正确的托管给父节点。从代码中的注释我们也可以看到,还有一种情况是当删除的节点是根节点并且这棵树要不只有左树要不只有右树,这种树在一开始遍历的时候parent节点就为空,所以当发现是这种情况直接就让根节点变成这个数的左子树或右子树即可。判断完前两种情况后我们就判断当要删除的节点左右子树都有的情况,我们以用右子树的最小值为例,这种情况有个特例:

【C++】:想知道如何实现互译字典吗?来看二叉搜索树

 当要删除8这个节点的时候,本来找右子树的最小值托管是找到其右子树然后一直往这棵树的左子树递归即可,然后还要有一个节点记录递归的最小子树的父节点,找到父节点让其托管右子树最小节点的右子树,但是如上图所示会有无左子树的情况,这种情况下prevparent如果初始化为空指针就会对空指针进行解引用,所以要将这个节点初始化为要删除的节点,这样才能解决上图中删8的情况。最后在托管的时候一定要判断右子树的最小值是其父节点的左子树还是右子树,这些条件都判断完后把要删除的节点释放即可。下面还有另一种实现删除的代码,大家容易理解哪个就写哪个:

bool erase(const K& val)
		{
			Node* cur = _root;
			Node* parent = nullptr;
			while (cur)
			{
				if (cur->_key < val)
				{
					parent = cur;
					cur = cur->right;
				}
				else if (cur->_key > val)
				{
					parent = cur;
					cur = cur->left;
				}
				else
				{
					//找到要删除的节点
					if (cur->left == nullptr)
					{
						if (parent == nullptr)
						{
							_root = cur->right;
							return true;
						}
						if (parent->left == cur)
						{
							parent->left = cur->right;
						}
						else
						{
							parent->right = cur->right;
						}
						delete cur;
						return true;
					}
					else if (cur->right == nullptr)
					{
						if (parent == nullptr)
						{
							_root = cur->left;
							return true;
						}
						if (parent->left == cur)
						{
							parent->left = cur->left;
						}
						else
						{
							parent->right = cur->left;
						}
						delete cur;
						return true;
					}
					else
					{
						//要删除的节点有两个子树,需要右树的最小节点或者左树的最大节点托管
						Node* minRight = cur->right;
						Node* PrevParent = cur;
						while (minRight->left)
						{
							PrevParent = minRight;
							minRight = minRight->left;
						}
						cur->_key = minRight->_key;
						if (PrevParent->left == minRight)
						{
							PrevParent->left = minRight->right;
						}
						else
						{
							PrevParent->right = minRight->right;
						}
						delete minRight;
						return true;
					}
				}
			}
			return false;
		}

4.查找

下面我们实现查找函数:

Node* find(const K& val)
		{
			Node* tmp = _find(_root, val);
			return tmp;
		}
Node* _find(Node* root, const K& val)
		{
			Node* cur = root;
			while (cur)
			{
				if (cur->_key < val)
				{
					cur = cur->right;
				}
				else if (cur->_key > val)
				{
					cur = cur->left;
				}
				else
				{
					return cur;
				}
			}
			return  nullptr;
		}

查找同样需要封装一下在使用,在实现查找函数的时候需要注意当根节点为空时返回nullptr即可。

5.用递归实现查找函数

Node* findR(const K& val)
		{
			Node* tmp = _findR(_root, val);
			return tmp;
		}

Node* _findR(Node* root, const K& val)
		{
			if (root == nullptr)
			{
				return nullptr;
			}
			if (root->_key == val)
			{
				return root;
			}
			if (root->_key < val)
			{
				return _findR(root->right, val);
			}
			else
			{
				return _findR(root->left, val);
			}
		}

递归实现起来其实代码会更简洁,当遇到空就返回空,找到了就返回节点,如果当前节点小于要查找的节点就递归当前节点的右子树查找,否则就去左子树查找。

6.用递归实现插入函数

bool insertR(const K& val)
		{
			bool result = _insertR(_root, val);
			return result;
		}
bool _insertR(Node*& root, const K& val)
		{
			if (root == nullptr)
			{
				root = new Node(val);
				return true;
			}
			if (root->_key < val)
			{
				return _insertR(root->right, val);
			}
			else if (root->_key > val)
			{
				return _insertR(root->left, val);
			}
			else
			{
				return false;
			}
		}

插入函数就是当要插入的节点大于当前节点就递归当前节点的右子树,否则就是左子树,当节点相等我们不插入返回false,等到当前节点为空我们就给当前节点new一个节点,插入成功后返回true即可,下面我们画个递归图解释为什么要用引用:

【C++】:想知道如何实现互译字典吗?来看二叉搜索树

 7.用递归实现删除函数

bool eraseR(const K& val)
		{
			bool result = _eraseR(_root, val);
			return result;
		}
bool _eraseR(Node*& root, const K& val)
		{
			if (root == nullptr)
			{
				return false;
			}
			if (root->_key < val)
			{
				return _eraseR(root->right, val);
			}
			else if (root->_key > val)
			{
				return _eraseR(root->left, val);
			}
			else
			{
				Node* del = root;
				if (root->left == nullptr)
				{
					root = root->right;
				}
				else if (root->right == nullptr)
				{
					root = root->left;
				}
				else
				{
					Node* maxLeft = root->left;
					while (maxLeft->right)
					{
						maxLeft = maxLeft->right;
					}
					std::swap(root->_key, maxLeft->_key);
					return _eraseR(root->left, val);
				}
				delete del;
				return true;
			}
		}

递归版的删除函数大致思想还是与我们刚刚的一样,只不过当我们要删除的时候情况变了,当要删除的节点左子树为空我们就将当前节点变成当前节点的右子树(因为等会会将这个要删除的节点释放),当要删除的节点的右子树为空我们就将当前节点变成当前节点的左子树。当要删除的节点两个子树都不为空时,我们以用左树的最大值托管为例,先创建一个节点为当前节点的左子树节点,然后让这个节点去遍历右子树,因为左子树的最大值一定在左子树的右子树中,遍历完直接将左子树的最大值和要删除的节点的值交换,因为已经交换了如下图:

【C++】:想知道如何实现互译字典吗?来看二叉搜索树

所以我们直接递归7的左子树删除这个为8的节点即可,在递归版需要注意的是节点一定要用引用,下面我们画个递归图解释:

【C++】:想知道如何实现互译字典吗?来看二叉搜索树 8.析构函数:

~BSTree()
		{
			Destruct(_root);
		}

void Destruct(Node*& root)
		{
			if (root == nullptr)
			{
				return;
			}
			Destruct(root->left);
			Destruct(root->right);
			delete root;
			root = nullptr;
		}

 析构函数的实现就很简单了,当遇到空就返回,否则就先递归左子树再递归右子树,最后是否root节点。也就是说整体就是一个后序遍历从最后一个节点开始删除,这里用引用的作用是当我们最后将root节点置为nullptr那么_root根节点也会被置为空。因为root是_root的别名。

9.构造函数

BSTree()
			:_root(nullptr)
		{

		}

构造函数只需要将根节点置为空即可。这里必须要实现的原因是等会要实现拷贝构造函数,如果我们不写构造函数当我们实现拷贝构造就不会生成默认的构造函数了。

10.拷贝构造函数

BSTree(const BSTree<K,V>& t)
		{
			_root = copy(t._root);
		}

Node* copy(Node* root)
		{
			if (root == nullptr)
			{
				return nullptr;
			}
			Node* newroot = new Node(root->_key);
			newroot->left = copy(root->left);
			newroot->right = copy(root->right);
			return newroot;
		}

拷贝构造的实现我们只需要用一个函数去用前序遍历依次创建节点即可,(默认的拷贝构造是浅拷贝)当节点为空就返回空指针,然后创建与一个newroot节点,此节点用传来的root节点的值初始化,然后分别递归建立左子树和右子树,最后返回新树即可。

11.赋值重载函数

BSTree<K,V>& operator=(BSTree<K,V> t)
		{
			std::swap(_root, t._root);
			return *this;
		}

赋值重载函数我们直接用现代写法,传参用传值传参,这样会拷贝构造一个树,然后我们直接交换这个被拷贝构造的数的根节点和我们的this指针指向的节点,然后返回即可,这里记得返回要引用返回否则还会多调用一次拷贝构造。

以上就是二叉搜索树的全部函数接口实现,下面我们讲讲二插搜索树的应用。

二、二叉搜索树的应用

1.K模型

. K 模型: K 模型即只有 key 作为关键码,结构中只需要存储 Key 即可,关键码即为需要搜索到
的值
比如: 给一个单词 word ,判断该单词是否拼写正确 ,具体方式如下:
以词库中所有单词集合中的每个单词作为 key ,构建一棵二叉搜索树
在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

2.KV模型

KV 模型:每一个关键码 key ,都有与之对应的值 Value ,即 <Key, Value> 的键值对 。该种方
式在现实生活中非常常见:
比如 英汉词典就是英文与中文的对应关系 ,通过英文可以快速找到与其对应的中文,英
文单词与其对应的中文 <word, chinese> 就构成一种键值对;
再比如 统计单词次数 ,统计成功后,给定单词就可快速找到其出现的次数, 单词与其出
现次数就是 <word, count> 就构成一种键值对。
对于KV模型我们给出以下两个例子:
1.输入单词,查找单词对应的中文翻译:
namespace key_value
{
	template <class K,class V>
	struct TreeNode
	{
		TreeNode<K,V>* left;
		TreeNode<K,V>* right;
		K _key;
		V _value;
		TreeNode(const K& key,const V& value)
			:left(nullptr)
			, right(nullptr)
			, _key(key)
			,_value(value)
		{

		}
	};
	template <class K,class V>
	class BSTree
	{
		typedef TreeNode<K,V> Node;
	public:
		BSTree()
			:_root(nullptr)
		{

		}
		BSTree(const BSTree<K,V>& t)
		{
			_root = copy(t._root);
		}
		BSTree<K,V>& operator=(BSTree<K,V> t)
		{
			std::swap(_root, t._root);
			return *this;
		}
		~BSTree()
		{
			Destruct(_root);
		}
		bool insert(const K& val, const V& value)
		{
			if (_root == nullptr)
			{
				_root = new Node(val,value);
				return true;
			}
			Node* cur = _root;
			Node* parent = nullptr;
			while (cur)
			{
				if (cur->_key < val)
				{
					parent = cur;
					cur = cur->right;
				}
				else if (cur->_key > val)
				{
					parent = cur;
					cur = cur->left;
				}
				else
				{
					return false;
				}
			}
			if (parent->_key < val)
			{
				parent->right = new Node(val,value);
			}
			else
			{
				parent->left = new Node(val,value);
			}
			return true;
		}
		bool erase(const K& val)
		{
			assert(_root != nullptr);
			Node* cur = _root;
			Node* parent = nullptr;
			while (cur)
			{
				if (cur->_key < val)
				{
					parent = cur;
					cur = cur->right;
				}
				else if (cur->_key > val)
				{
					parent = cur;
					cur = cur->left;
				}
				else
				{
					//找到要删除的节点
					if (cur->left == nullptr)
					{
						if (parent == nullptr)
						{
							_root = cur->right;
							return true;
						}
						if (parent->left == cur)
						{
							parent->left = cur->right;
						}
						else
						{
							parent->right = cur->right;
						}
						delete cur;
						return true;
					}
					else if (cur->right == nullptr)
					{
						if (parent == nullptr)
						{
							_root = cur->left;
							return true;
						}
						if (parent->left == cur)
						{
							parent->left = cur->left;
						}
						else
						{
							parent->right = cur->left;
						}
						delete cur;
						return true;
					}
					else
					{
						//要删除的节点有两个子树,需要右树的最小节点或者左树的最大节点托管
						Node* minRight = cur->right;
						Node* PrevParent = cur;
						while (minRight->left)
						{
							PrevParent = minRight;
							minRight = minRight->left;
						}
						cur->_key = minRight->_key;
						if (PrevParent->left == minRight)
						{
							PrevParent->left = minRight->right;
						}
						else
						{
							PrevParent->right = minRight->right;
						}
						delete minRight;
						return true;
					}
				}
			}
			return false;
		}
		Node* find(const K& val)
		{
			Node* tmp = _find(_root, val);
			return tmp;
		}
		void Inorder()
		{
			_Inorder(_root);
		}
	protected:
		Node* copy(Node* root)
		{
			if (root == nullptr)
			{
				return nullptr;
			}
			Node* newroot = new Node(root->_key);
			newroot->left = copy(root->left);
			newroot->right = copy(root->right);
			return newroot;
		}
		void Destruct(Node*& root)
		{
			if (root == nullptr)
			{
				return;
			}
			Destruct(root->left);
			Destruct(root->right);
			delete root;
			root = nullptr;
		}
		Node* _find(Node* root, const K& val)
		{
			Node* cur = root;
			while (cur)
			{
				if (cur->_key < val)
				{
					cur = cur->right;
				}
				else if (cur->_key > val)
				{
					cur = cur->left;
				}
				else
				{
					return cur;
				}
			}
			return  nullptr;
		}
		void _Inorder(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			_Inorder(root->left);
			cout << root->_key << ":"<<root->_value<<" ";
			_Inorder(root->right);
		}

	private:
		Node* _root = nullptr;
	};
}
void test5()
{
	key_value::BSTree<string, string> dict;
	dict.insert("sort", "排序");
	dict.insert("insert", "插入");
	dict.insert("left", "左边");
	dict.insert("right", "右边");
	dict.insert("erase", "删除");
	string str;
	while (cin >> str)
	{
		auto ret = dict.find(str);
		if (ret)
		{
			cout << ":"<<ret->_value << endl;
		}
		else
		{
			cout << "找不到此单词" << endl;
		}
	}
}

我们看看上述代码的结果:

【C++】:想知道如何实现互译字典吗?来看二叉搜索树

KV的实现只需要添加一个模板参数,修改遍历中序打印函数和插入函数,插入的时候我们需要将新参数也插入,我们在节点中初始化的时候记得初始化新的参数即可。

简单的中文互译我们就搞定了,再看看统计次数的例子 。

2.统计水果出现的次数:
void test6()
{
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
"苹果", "香蕉", "苹果", "香蕉" };
	key_value::BSTree<string, int> ct;
	for (auto& ch : arr)
	{
		auto ret = ct.find(ch);
		if (ret)
		{
			ret->_value++;
		}
		else
		{
			ct.insert(ch, 1);
		}
	}
	ct.Inorder();
}

【C++】:想知道如何实现互译字典吗?来看二叉搜索树

统计次数很简单,当数组中的水果在树中存在我们就让水果的次数++,如果不存在我们就将这个水果插入数中,最后打印出来的顺序是按照ascll码进行比较的,因为中文的底层是ascll。 


总结

二叉搜索树的性能分析:文章来源地址https://www.toymoban.com/news/detail-429995.html

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
对有 n 个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二
叉搜索树的深度的函数,即结点越深,则比较次数越多。
但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树。
最优情况下,二叉搜索树为完全二叉树 ( 或者接近完全二叉树 ) ,其平均比较次数为:O(log N)
最差情况下,二叉搜索树退化为单支树 ( 或者类似单支 ) ,其平均比较次数为:O(N)

到了这里,关于【C++】:想知道如何实现互译字典吗?来看二叉搜索树的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++】二叉搜索树的模拟实现

    🌇个人主页:平凡的小苏 📚学习格言:命运给你一个低的起点,是想看你精彩的翻盘,而不是让你自甘堕落,脚下的路虽然难走,但我还能走,比起向阳而生,我更想尝试逆风翻盘 。 🛸 C++专栏 : C++内功修炼基地 家人们更新不易,你们的👍点赞👍和⭐关注⭐真的对我真

    2024年02月13日
    浏览(37)
  • 【C++】二叉搜索树的原理及实现

      二叉搜索树(Binary Search Tree,BST)是一种常用的数据结构,本文将介绍二叉搜索树的原理与特性,并给出C++代码实现,最后对其性能进行详细的分析。    文章目录 简介 一、二叉搜索树的概念 二、二叉搜索树的操作及实现 2、1 二叉搜索树的插入 2、1、1 插入的原理 2、1、2

    2024年02月14日
    浏览(53)
  • 【数据结构进阶】之搜索二叉树(C++实现)

    📃 博客主页: 小镇敲码人 💞 热门专栏:数据结构与算法 🚀 欢迎关注:👍点赞 👂🏽留言 😍收藏 🌏 任尔江湖满血骨,我自踏雪寻梅香。 万千浮云遮碧月,独傲天下百坚强。 男儿应有龙腾志,盖世一意转洪荒。 莫使此生无痕度,终归人间一捧黄。🍎🍎🍎 ❤️ 什么

    2024年04月08日
    浏览(48)
  • 【数据结构】—搜索二叉树(C++实现,超详细!)

                                                           🎬 慕斯主页 : 修仙—别有洞天                                                        ♈️ 今日夜电波 :消えてしまいそうです—真夜中                                              

    2024年02月05日
    浏览(40)
  • 【C++】平衡二叉搜索树的模拟实现

    🌇个人主页:平凡的小苏 📚学习格言:命运给你一个低的起点,是想看你精彩的翻盘,而不是让你自甘堕落,脚下的路虽然难走,但我还能走,比起向阳而生,我更想尝试逆风翻盘 。 🛸 C++专栏 : C++内功修炼基地 家人们更新不易,你们的👍点赞👍和⭐关注⭐真的对我真

    2024年02月09日
    浏览(33)
  • 数据结构——二叉搜索树(附带C++实现版本)

    二叉搜索树又叫二叉排序树 ,二叉搜索树也是一种树形结构。 它是一课满足以下性质的搜索树: 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值 它的左右子树也分别是二叉搜索树 注意,二

    2024年02月12日
    浏览(38)
  • C++------利用C++实现二叉搜索树【数据结构】

    什么是二叉搜索树,二叉搜索树就是指左孩子永远比根小右孩子永远比根大。这个规则适用于所有的子树。 上面的就是一棵二叉搜索树,我们还可以发现这棵树走一个中序遍历序列是有序的,所以它又被称为二叉排序树。 二叉搜索树的操作主要分为以下几点,查找, 插入,

    2024年02月11日
    浏览(41)
  • [数据结构进阶 C++] 二叉搜索树(BinarySearchTree)的模拟实现

    二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树: 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值 它的左右子树也分别为二叉搜索树 我们先给出两个示例: 此

    2024年02月04日
    浏览(42)
  • 【C++】AVL(平衡二叉搜索树)树的原理及实现

    文章目录 一、引言 二、AVL树的概念 三、AVL树的插入 3、1 AVL树的简单插入 3、2 旋转分类  3、2、1 新节点插入较高右子树的右侧:左单旋 3、2、2 新节点插入较高左子树的左侧:右单旋 3、2、3 新节点插入较高左子树的右侧:左右双旋(先左后右) 3、2、4 新节点插入较高右

    2024年02月13日
    浏览(49)
  • C++ 二叉搜索树(BST)的实现(非递归版本与递归版本)与应用

    二叉搜索树既可以实现为升序版本,也可以实现为降序版本 本文实现为升序版本 二叉搜索树是一种特殊的二叉树 它的特点是: 1.左子树的所有节点均比根节点的值小 2.右子树的所有节点均比根节点的值大 3.左右子树都是二叉搜索树 4.中序遍历序列是有序的 5.一般二叉搜索树不

    2024年02月04日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包