二叉搜索树详解及代码实现

目录

一、什么是二叉搜索树

 二、二叉搜索树的有关操作

2.1 查找:

2.2插入:

2.3 删除:

 2.4 打印

三、二叉搜索树的应用

3.1 K模型:

3.2 KV模型:

四、整体代码:

K模型:

KV模型:


一、什么是二叉搜索树

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

每一棵搜索树都满足如下条件:

1.非空左子树的所有键值小于其根节点的键值

2.非空右子树的所有键值大于其根节点的键值

3.左、右子树都是二叉搜索树

 如图所示二叉树:

 二、二叉搜索树的有关操作

如该树 数组表示为:int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13}; 用此树进行讲解

下面代码用到的c++类结构为:

	template<class K>  //模板
	struct BSTreeNode {
		BSTreeNode<K>* _left;
		BSTreeNode<K>* _right;
		K _key;
	private:
		Node* _root = nullptr;
	};

2.1 查找:

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

代码实现:

非递归:

		bool Find(const K& key)  //K为模板
		{
			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(Node* root, const K& key)
		{
			if (root == nullptr)
				return false;

			if (root->_key < key)
				return _FinfR(root->_right, key);
			else if (root->_key > key)
				return _FindR(root->_left, key);
			else
				return true;
		}

2.2插入:

插入的具体过程如下:
a. 树为空,则直接新增节点,赋值给root指针
b. 树不空,按二叉搜索树性质查找插入位置,插入新节点(大于根节点值往右走,小于根节点值往左走)。注意:二叉搜索树插入过程中,插入已存在值无意义。

为该树插入16 、9 两个值,根据搜素树应满足的“左小右大”的条件,插入后如图:

 代码实现:

非递归写法:

bool Insert(const K& key)    //K为模板
		{
            //空树直接插
			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(Node*& root, const K& key) //K为模板
		{
			if (root == nullptr)
			{
				root = new Node(key);
				return true;
			}

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

2.3 删除:

删除某一节点可分为四种情况:

a.要删除节点没有孩子节点

如图删除节点值13,没有孩子节点,删除后父节点parent->left就指向空。

 b.要删除的结点只有左孩子结点 

c. 要删除的结点只有右孩子结点

该两种情况相似。如删除节点14后,其孩子节点13的父节点就变为10,即:10->right=13

d. 要删除的结点有左、右孩子结点

该情况难点在于删除节点后,被删除节点的父节点应该链接被删除节点的哪个孩子节点。这分为两种方法:

1.连接删除节点的左子树中最右边节点

2.连接删除节点的右子树中最左节点

一般情况下,我们使用方法2.在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题--替换法删除


 代码实现:
非递归:

		bool Erase(const K& key)
		{
			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
				{
					//开始删除
					//删除位置左空
					if (cur->_left == nullptr)
					{
						if (cur == _root)//删除位置在根处
						{
							_root = cur->_right;
						}
						else
						{
							//找到当前位置后且左空,且位于parent的左,
							//则删除该位置后的parent左就变为cur的右
							//如果当前位置位于parren右,且且左空,则paret的右即为cur的右
							if (cur == parent->_left)
							{
								parent->_left = cur->_right;
							}
							else
							{
								parent->_right = cur->_right;
							}
						}
						delete cur;
						cur == nullptr;
					}
					else if (cur->_right == nullptr)//删除位置右空
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (cur == parent->_left)
							{
								parent->_left = cur->_left;
							}
							else
							{
								parent->_right = cur->_left;
							}
						}
						delete cur;
						cur = nullptr;
					}
					else//左右都不为空
					{
						//替换法删除  //右子树最小节点---右子树最左节点
						Node* minparent = cur;
						Node* min = cur->_right;
						while (min->_left)
						{
							minparent = min;
							min = min->_left;
						}
						swap(cur->_key, min->_key);
						if (minparent->_left == min)
							minparent->_left = min->_right;
						else
							minparent->_right = min->_right;
						delete min;
					}
					return true;
				}
			}
			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* min = root->_right;
					while (min->_left)
					{
						min = min->_left;
					}
					swap(root->_key, min->_key);
					return _EraseR(root->_right, key);
				}

				delete del;
				return true;
			}
		}

 2.4 打印

这里可以将创建的二叉搜索树通过中序(左->根->右)打印出来,就是由小到大的升序序列。因为二叉搜索树中不能有重复的数值,所以可以通过插入一组数据实现对其排序且去重。

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

		}

该二叉搜索树的整体代码放在文末。

三、二叉搜索树的应用

3.1 K模型:

上面我们所实现的就是K模型。

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

3.2 KV模型:

KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。一般可以通过查找关键码key来查找对应的值。

比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英
文单词与其对应的中文<word, chinese>就构成一种键值对。(KV模型整体代码与K模型思想很相似,就直接放在文末)

	void TestBSTree1()
	{
		BSTree<string, string> dict;
		dict.Insert("sort", "排序");
		dict.Insert("left", "左边");
		dict.Insert("right", "右边");
		dict.Insert("string", "字符串");
		dict.Insert("insert", "插入");
		string str;
		while (cin >> str)
		{
			BSTreeNode<string, string>* ret = dict.Find(str);
			if (ret)
			{
				cout << ":" << ret->_value << endl;
			}
			else
			{
				cout << "->无此单词" << endl;
			}
		}
	}

 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出
现次数就是<word, count>就构成一种键值对。

void TestBSTree2()
	{
		string arr[] = { "苹果","苹果" ,"香蕉" ,"草莓" ,"香蕉" ,"苹果" ,"苹果", "苹果" };
		
		BSTree<string, int> countTree;
		for (auto& str:arr)
		{
			auto ret = countTree.Find(str);
			if (ret)
			{
				ret->_value++;
			}
			else
			{
				countTree.Insert(str, 1);
			}
		}

		countTree.InOrder();
	} 

四、整体代码:

K模型:

namespace key
{
	template<class K>
	struct BSTreeNode {
		BSTreeNode<K>* _left;
		BSTreeNode<K>* _right;
		K _key;

		BSTreeNode(const K& key)
			:_left(nullptr)
			, _right(nullptr)
			, _key(key)
		{}
	};


	//BinarySearchTree
	template<class K>   //Key
	class BSTree {
		typedef BSTreeNode<K> Node;
	public:
		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 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 Erase(const K& key)
		{
			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
				{
					//开始删除
					//删除位置左空
					if (cur->_left == nullptr)
					{
						if (cur == _root)//删除位置在根处
						{
							_root = cur->_right;
						}
						else
						{
							//找到当前位置后且左空,且位于parent的左,
							//则删除该位置后的parent左就变为cur的右
							//如果当前位置位于parren右,且且左空,则paret的右即为cur的右
							if (cur == parent->_left)
							{
								parent->_left = cur->_right;
							}
							else
							{
								parent->_right = cur->_right;
							}
						}
						delete cur;
						cur == nullptr;
					}
					else if (cur->_right == nullptr)//删除位置右空
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (cur == parent->_left)
							{
								parent->_left = cur->_left;
							}
							else
							{
								parent->_right = cur->_left;
							}
						}
						delete cur;
						cur = nullptr;
					}
					else//左右都不为空
					{
						//替换法删除  //右子树最小节点---右子树最左节点
						Node* minparent = cur;
						Node* min = cur->_right;
						while (min->_left)
						{
							minparent = min;
							min = min->_left;
						}
						swap(cur->_key, min->_key);
						if (minparent->_left == min)
							minparent->_left = min->_right;
						else
							minparent->_right = min->_right;
						delete min;
					}
					return true;
				}
			}
			return false;
		}

		void InOrder()
		{
			_InOrder(_root);//这里套一层就可以直接使用_root;或者也可以写getroot方法
			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);
		}
		//强制编译器生成默认的构造
		BSTree() = default;
		BSTree(const BSTree<K>& t)
		{
			_root = _copy(t._root);
		}

		BSTree<K>& operator=(BSTree<K> t)
		{
			swap(_root, t._root);
			return *this;
		}
	private:
		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;
		}
		void _Destroy(Node*& root)
		{
			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->_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* min = root->_right;
					while (min->_left)
					{
						min = min->_left;
					}
					swap(root->_key, min->_key);
					return _EraseR(root->_right, key);//很巧妙
				}

				delete del;
				return true;
			}
		}
		bool _InsertR(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				root = new Node(key);
				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 _FindR(Node* root, const K& key)
		{
			if (root == nullptr)
				return false;

			if (root->_key < key)
				return _FinfR(root->_right, key);
			else if (root->_key > key)
				return _FindR(root->_left, key);
			else
				return true;
		}
		//中序  //
		void _InOrder(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			_InOrder(root->_left);
			cout << root->_key << " ";
			_InOrder(root->_right);

		}
	private:
		Node* _root = nullptr;
	};
}

KV模型:

template<class K,class V>
	struct BSTreeNode {
		BSTreeNode<K,V>* _left;
		BSTreeNode<K,V>* _right;
		K _key;
		V _value;

		BSTreeNode(const K& key, const V& value)
			:_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;
			}

			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,value);
			if (parent->_key < key)
			{
				parent->_right = cur;
			}
			else
			{
				parent->_left = cur;
			}
			return true;
		}
		Node* 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 cur;
				}
			}
			return nullptr;
		}

		bool Erase(const K& key)
		{
			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
				{
					//开始删除
					//删除位置左空
					if (cur->_left == nullptr)
					{
						if (cur == _root)//删除位置在根处
						{
							_root = cur->_right;
						}
						else
						{
							//找到当前位置后且左空,且位于parent的左,
							//则删除该位置后的parent左就变为cur的右
							//如果当前位置位于parren右,且且左空,则paret的右即为cur的右
							if (cur == parent->_left)
							{
								parent->_left = cur->_right;
							}
							else
							{
								parent->_right = cur->_right;
							}
						}
						delete cur;
						cur == nullptr;
					}
					else if (cur->_right == nullptr)//删除位置右空
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (cur == parent->_left)
							{
								parent->_left = cur->_left;
							}
							else
							{
								parent->_right = cur->_left;
							}
						}
						delete cur;
						cur = nullptr;
					}
					else//左右都不为空
					{
						//替换法删除  //右子树最小节点---右子树最左节点
						Node* minparent = cur;
						Node* min = cur->_right;
						while (min->_left)
						{
							minparent = min;
							min = min->_left;
						}
						swap(cur->_key, min->_key);
						if (minparent->_left == min)
							minparent->_left = min->_right;
						else
							minparent->_right = min->_right;
						delete min;
					}
					return true;
				}
			}

			//删除的中间过程都一样
			return true;
		}

		void InOrder()
		{
			_InOrder(_root);//这里套一层就可以直接使用_root;或者也可以写getroot方法
			cout << endl;
		}

	private:
		//中序  //
		void _InOrder(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			_InOrder(root->_left);
			cout << root->_key << " value: " << root->_value << endl;;
			_InOrder(root->_right);

		}
	private:
		Node* _root = nullptr;
	};

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值