二叉搜索树 --- C++实现

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

目录

1.二叉搜索树的概念

2.二叉搜索树的操作

3. 二叉树的实现

4.二叉搜索树的应用

5. 二叉树的性能分析

6. 二叉树进阶练习题


1.二叉搜索树的概念

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

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

二叉搜索树(BST,Binary Search Tree),也称二叉排序树或二叉查找树

2.二叉搜索树的操作

二叉搜索树 --- C++实现,高阶数据结构,算法,c++,二叉树,二叉搜索树

int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13};

1. 二叉搜索树的查找

  • a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
  • b、最多查找高度次,走到到空,还没找到,这个值不存在。

2. 二叉搜索树的插入

    插入的具体过程如下:

  • a. 树为空,则直接新增节点,赋值给root指针
  • b. 树不空,按二叉搜索树性质查找插入位置,插入新节点
     

3. 二叉搜索树的删除

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

  • a. 要删除的结点无孩子结点
  • b. 要删除的结点只有左孩子结点
  • c. 要删除的结点只有右孩子结点
  • d. 要删除的结点有左、右孩子结点

        看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,把一个孩子看作空节点,因此真正的删除过程如下:

  • 情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点,然后直接删除该节点 -- 即直接删除。
  • 情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点,然后直接删除该节点 -- 即直接删除。
  • 情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题 -- 替换法删除。即用左子树的最大节点,或右子树的最小节点来替换。

3. 二叉树的实现

代码中有每个操作都有两种写法,一种是非递归写法,一种是递归写法。 

namespace key
{
	template<class K>
	struct BSTreeNode
	{
		BSTreeNode<K>* left;
		BSTreeNode<K>* right;
		K _key;

		BSTreeNode(const K& key = K())
			:left(nullptr)
			, right(nullptr)
			, _key(key)
		{
		}

	};

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

		BSTree()
			:_root(nullptr)
		{
		}

		//C++11 强制生成默认构造
		//BSTree() = default; 

		BSTree(const BSTree<K>& root)
		{
			_root = Copy(root._root);
		}

		BSTree<K>& operator=(BSTree<K> tree)
		{
			swap(tree._root, _root);
			return *this;
		}


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

		bool Find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key == key)
				{
					return true;
				}
				else if (cur->_key < key)
				{
					cur = cur->right;
				}
				else
				{
					cur = cur->left;
				}
			}
			return false;
		}

		bool Erase(const K& key)//删除
		{
			Node* cur = _root;
			Node* parent = nullptr;
			while (cur)
			{
				if (cur->_key > key)
				{
					parent = cur;
					cur = cur->left;
				}
				else if (cur->_key < key)
				{
					parent = cur;
					cur = cur->right;
				}
				else//找到删除
				{
					if (cur->left == nullptr)
					{
						//左为空
						if (parent == nullptr)//根节点
						{
							_root = cur->right;
						}
						else
						{
							if (cur == parent->left)
							{
								parent->left = cur->right;
							}
							else if (cur == parent->right)
							{
								parent->right = cur->right;
							}
							delete cur;
						}
					}
					else if (cur->right == nullptr)
					{
						//右为空
						if (parent == nullptr)//根节点
						{
							_root = cur->left;
						}
						else
						{
							if (cur == parent->left)
							{
								parent->right = cur->left;
							}
							else if (cur == parent->right)
							{
								parent->right = cur->left;
							}
							delete cur;
						}
					}
					else
					{//左右都不为空
						//右树的最小节点(最左节点)
						Node* subLeft = cur->right;
						Node* parent = cur;
						while (subLeft->left)
						{
							parent = subLeft;
							subLeft = subLeft->left;
						}
						if (subLeft == cur->right)//右树的最左节点是根,最左节点不是父节点的左孩子
						{
							swap(subLeft->_key, cur->_key);
							parent->right = subLeft->right;
						}
						else
						{
							swap(subLeft->_key, cur->_key);
							parent->left = subLeft->right;
						}
						delete subLeft;
					}
					return true;
				}

			}
			return false;
		}


		//递归遍历
		void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}
		//递归查找

		bool FindR(const K& key)
		{
			return _FindR(_root, key);
		}

		//递归插入
		bool InsertR(const K& key)
		{
			return _InsertR(_root, key);
		}
		//递归删除
		bool EraseR(const K& key)
		{
			return _EraseR(_root, key);
		}

		~BSTree()
		{
			Destroy(_root);
		}

	private:
		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 Destroy(Node*& root)//auto不能做函数参数
		{
			if (root == nullptr)
			{
				return;
			}
			Destroy(root->left);
			Destroy(root->right);
			delete root;
			root = nullptr;
		}


		bool _EraseR(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				return false;
			}
			if (root->_key > key)
			{
				return _EraseR(root->left, key);
			}
			if (root->_key < key)
			{
				return _EraseR(root->right, key);
			}
			else
			{
				//删除,用引用十分巧妙
				if (root->right == nullptr)
				{//引用的是父节点的一个指针
					Node* del = root;
					root = root->left;
					delete del;
					return true;
				}
				else if (root->left == nullptr)
				{
					Node* del = root;
					root = root->right;
					delete del;
					return true;
				}
				else
				{
					Node* subLeft = root->right;

					while (subLeft->left)
					{
						subLeft = subLeft->left;
					}
					swap(subLeft->_key, root->_key);
					//让子树递归删除 
					return _EraseR(root->right, key);

				}
			}
		}

		bool _InsertR(Node*& root, const K& key)
		{
			//这里root用引用
			if (root == nullptr)
			{
				root = new Node(key);
				return true;
			}
			if (root->_key < key)
			{//引用的是父节点的右孩子
				_InsertR(root->right, key);
			}
			if (root->_key > key)
			{
				_InsertR(root->left, key);
			}
			else
			{
				return false;
			}
		}
		//bool _InsertR(Node* root, const K& key)
		//{
		//	if (root == nullptr)
		//	{
		//		_root = new Node(key);
		//		return true;
		//	}
		//	if (key == root->_key)
		//	{
		//		return false;
		//	}
		//	if (key < root->_key)
		//	{
		//		if (root->left == nullptr)
		//		{
		//			root->left = new Node(key);
		//			return true;
		//		}
		//		else
		//		{
		//			return _InsertR(root->left, key);
		//		}
		//	}
		//	if (key > root->_key)
		//	{
		//		if (root->right == nullptr)
		//		{
		//			root->right = new Node(key);
		//			return true;
		//		}
		//		else
		//		{
		//			return _InsertR(root->right, key);
		//		}
		//	}
		//}

		bool _FindR(Node* root, const K& key)
		{
			if (root == nullptr)
			{
				return false;
			}
			else if (root->_key == key)
			{
				return true;
			}
			else if (root->_key < key)
			{
				return _FindR(root->right, key);
			}
			else
			{
				return _FindR(root->left, key);
			}
		}

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

	private:
		Node* _root = nullptr;
	};
}

4.二叉搜索树的应用

1. K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。

比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:

  1. 以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
  2. 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

2. KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方式在现实生活中非常常见:

  1. 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对;
  2. 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对
     
//改造后的 key_value 结构
namespace key_value
{
	template<class K, class V>
	struct BSTreeNode
	{
		BSTreeNode<K,V>* left;
		BSTreeNode<K,V>* right;
		K _key;
		V _value;

		BSTreeNode(const K& key = K(),const V& value = V())
			:left(nullptr)
			, right(nullptr)
			, _key(key)
			, _value(value)
		{
		} 

	};

	template<class K, class V>
	class BSTree
	{
		typedef BSTreeNode<K,V> Node;
	public:

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

		Node* Find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key == key)
				{
					return cur;
				}
				else if (cur->_key < key)
				{
					cur = cur->right;
				}
				else
				{
					cur = cur->left;
				}
			}
			return nullptr;
		}

		bool Erase(const K& key)
		{
			Node* cur = _root;
			Node* parent = nullptr;
			while (cur)
			{
				if (cur->_key > key)
				{
					parent = cur;
					cur = cur->left;
				}
				else if (cur->_key < key)
				{
					parent = cur;
					cur = cur->right;
				}
				else//找到删除
				{
					if (cur->left == nullptr)
					{
						//左为空
						if (parent == nullptr)//根节点
						{
							_root = cur->right;
						}
						else
						{
							if (cur == parent->left)
							{
								parent->left = cur->right;
							}
							else if (cur == parent->right)
							{
								parent->right = cur->right;
							}
							delete cur;
						}
					}
					else if (cur->right == nullptr)
					{
						//右为空
						if (parent == nullptr)//根节点
						{
							_root = cur->left;
						}
						else
						{
							if (cur == parent->left)
							{
								parent->right = cur->left;
							}
							else if (cur == parent->right)
							{
								parent->right = cur->left;
							}
							delete cur;
						}
					}
					else
					{//左右都不为空
						//右树的最小节点(最左节点)
						Node* subLeft = cur->right;
						Node* parent = cur;
						while (subLeft->left)
						{
							parent = subLeft;
							subLeft = subLeft->left;
						}
						if (subLeft == cur->right)//右树的最左节点是根,最左节点不是父节点的左孩子
						{
							swap(subLeft->_key, cur->_key);
							parent->right = subLeft->right;
						}
						else
						{
							swap(subLeft->_key, cur->_key);
							parent->left = subLeft->right;
						}
						delete subLeft;
					}
					return true;
				}

			}
			return false;
		}


		//递归遍历
		void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}
	private:
	

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

	private:
		Node* _root = nullptr;
	};
}

5. 二叉树的性能分析

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。

对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。

但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

二叉搜索树 --- C++实现,高阶数据结构,算法,c++,二叉树,二叉搜索树

最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其时间复杂度为:O(logN)

最差情况下,二叉搜索树退化为单支树(或者类似单支),其时间复杂度为:O(N)

问题:如果退化成单支树,二叉搜索树的性能就失去了。那能否进行改进,不论按照什么次序插入关键码,二叉搜索树的性能都能达到最优?那么我们后续文章学习的AVL树和红黑树就可以上场了。

6. 二叉树进阶练习题

这些题目更适合使用C++完成,难度也更大一些

  1. 二叉树创建字符串。OJ链接
  2. 二叉树的分层遍历1。OJ链接
  3. 二叉树的分层遍历2。OJ链接
  4. 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先 。OJ链接
  5. 二叉树搜索树转换成排序双向链表。OJ链接
  6. 根据一棵树的前序遍历与中序遍历构造二叉树。OJ链接
  7. 根据一棵树的中序遍历与后序遍历构造二叉树。OJ链接
  8. 二叉树的前序遍历,非递归迭代实现 。OJ链接
  9. 二叉树中序遍历 ,非递归迭代实现。OJ链接
  10. 二叉树的后序遍历 ,非递归迭代实现。OJ链接

本篇详细代码可查看我的Gitee

本篇结束!文章来源地址https://www.toymoban.com/news/detail-763356.html

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

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

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

相关文章

  • [数据结构进阶 C++] 二叉搜索树(BinarySearchTree)的模拟实现

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

    2024年02月04日
    浏览(42)
  • C++数据结构之平衡二叉搜索树(一)——AVL的实现(zig与zag/左右双旋/3+4重构)

    本文是介绍众多平衡二叉搜索树(BBST)的第一篇——介绍AVL树。故先来引入BBST的概念。由于上一篇介绍的二叉搜索树(BST)在极度退化的情况下,十分不平衡,不平衡到只朝一侧偏,成为一条链表,复杂度可达 O ( n ) O(n) O ( n ) ,所以我们要在“平衡”方面做一些约束,以防

    2024年02月13日
    浏览(38)
  • C++数据结构与算法详解:链表、栈、队列、树、二叉树和图结构的实现与应用

    链表是一种常见的数据结构由一系列节点按顺序连接而成,一般每个节点包含一个数据元素和一个指向下一个节点的引用。 链表有多种类型: 单向链表:每个节点只有一个指向下一个节点的引用 双向链表:每个节点有一个指向前一个节点和一个指向后一个节点的引用 循环链

    2024年02月04日
    浏览(52)
  • 【C++&数据结构】超详细一文带小白轻松全面理解 [ 二叉搜索树 ]—— [从零实现&逐过程分析&代码演示&简练易懂](23)

    前言 大家好吖,欢迎来到 YY 滴数据结构系列 ,热烈欢迎! 本章主要内容面向接触过C++的老铁 主要内容含: 欢迎订阅 YY 滴C++专栏!更多干货持续更新!以下是传送门! YY的《C++》专栏 YY的《C++11》专栏 YY的《Linux》专栏 YY的《数据结构》专栏 YY的《C语言基础》专栏 YY的《初

    2024年02月05日
    浏览(114)
  • 算法与数据结构--二叉搜索树与自平衡二叉搜索树

    注:字典的 \\\"member运算\\\" 指的是检查字典中是否存在某个特定的键的操作,即查询操作。 如果我们使用数组来实现字典/map,虽然使用二分法查询也可以达到logn,但是的话插入和删除太慢了。使用链表实现的话虽然插入和删除是O(1),但是查询的话达到了O(n),也不可取。 因此人

    2024年02月04日
    浏览(38)
  • 【数据结构(C++)】树型查找——二叉搜索树

    目录 1. 二叉搜索树 1.1 二叉搜索树的概念 1.2 二叉搜索树类模板 1.3 二叉搜索树的操作 1.3.1 查找 1.3.2 插入 1.3.3 删除 1.4 二叉搜索树的性能分析 2. 平衡二叉树 2.1 平衡二叉树的概念 2.2 平衡二叉树类模板 2.3 二叉搜索树的插入 3. 红黑树 3.1 红黑树的概念 3.2 红黑树类模板 二叉搜索

    2024年02月10日
    浏览(43)
  • Java数据结构与算法:二叉搜索树

    Java数据结构与算法:二叉搜索树 大家好,我是免费搭建查券返利机器人赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿! 在计算机科学中,二叉搜索树(Binary Search Tree,简称BST)是一种常见的树形数据结构,它具有良好的查找和插入性能。每

    2024年01月24日
    浏览(45)
  • 【算法与数据结构】700、LeetCode二叉搜索树中的搜索

    所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。    思路分析 :二叉搜索树(Binary Search Tree, BST)的性质:所有左子树节点键值 中间节点键值 所有右子树节点键值,并且左右子树都是二叉搜索树。那么我们根据此性质,对比目标值和中间节点,如

    2024年02月10日
    浏览(41)
  • 数据结构与算法-基础(十)平衡二叉搜索树

    摘要 二叉搜索树的特性-节点的左侧部分比它小,右侧部分比它大,使得二叉搜索树在查找节点有 二分法 的效果,也提高了它的添加和删除处理,毕竟添加和删除也是先查找位置,然后再处理。 平衡二叉搜索树 就是持续保证这样的高效性,进入正题: 二叉搜索树 在添加或

    2024年02月08日
    浏览(48)
  • 数据结构与算法—二叉树数组表示(ArrayBinTree)、二叉树的链式表示(LinkedBinTree)的基于C++模板代码实现

    1、二叉树的顺序表示:ArrayBinTree.h 二叉树的顺序表示法操作方便,但缺点是容易造成存储空间浪费。 这是一个用数组实现的二叉树类模板。它可以创建一个空树,也可以在指定的位置插入结点并设置结点的值,可以删除子树,并支持逐层遍历。使用该类时,需要提供一个元

    2024年02月06日
    浏览(44)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包