定义
二叉搜索树(BST,Binary Search Tree)
- 或为一颗空树,或满足一下性质
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树
注:二叉树中元素是单一的,无重复
例子
- arr = { 8,3,1,4,6,10,7,14,13}
  
二叉搜索树的功能及实现
节点
struct BSNode
{
	BSNode(const K& data)
		:_left(nullptr)
		, _right(nullptr)
		, _key(data)
	{}
	BSNode<K>* _left;
	BSNode<K>* _right;
	K _key;
};
如上文讨论,二叉搜索树是一种二叉树,节点与普通二叉树相似
基本结构
class BinarySearchTree
{
	typedef BSNode<K> Node;
	typedef Node* PNode;
	public:
		//构造函数
		BinarySearchTree();
		//拷贝构造
		BinarySearchTree(const BinarySearchTree<K>& t);
		//赋值重载
		BinarySearchTree<K>& operator=(BinarySearchTree<K> t);
		//析构函数
		~BinarySearchTree();
		//插入
		bool Insert(const K& data);
		//插入的递归实现
		bool InsertR(const K& data);
		//中序遍历
		void InOrder();
		//删除
		bool Erase(const K& data);
		//删除的递归实现
		bool EraseR(const K& data);
		//查找
		bool Find(const K& data);
		//查找的递归实现
		bool FindR(const K& data);
		//树的销毁(用以辅助析构函数)
		void Destory();
		private:
			PNode _root = nullptr;
	};
构造函数
BinarySearchTree()
		{}
因以给与_root以默认值,构造函数起始可以省略
 该默认值等效为
BinarySearchTree():_root(nullptr)
		{}
使用构造函数构造一个空树即可
拷贝构造
拷贝一棵新树即可
		BinarySearchTree(const BinarySearchTree<K>& t)
		{
			_root = _Copy(t._root);//调用_Copy函数递归构造树
		}
		PNode _Copy(PNode root)
		{
			if (root == nullptr)//当root节点为空树时,返回nullptr
			return nullptr;
			//拷贝根节点
			PNode CopyRoot = new Node(root->_key);
			//拷贝并链接左子树
			CopyRoot->_left = _Copy(root->_left);
			//拷贝并链接右子树
			CopyRoot->_right = _Copy(root->_right);
			//返回新拷贝出的子树
			return CopyRoot;
	}
赋值重载
因拷贝构造已经实现,在函数传参时,非传引用情况下,函数参数会调用拷贝构造去构造一棵新树,交换指针即可
 
拷贝出的树会于函数结束后自动析构
		BinarySearchTree<K>& operator=(BinarySearchTree<K> t)
		{
			std::swap(_root, t._root);
			return *this;
		}
析构函数
析构函数也通过递归调用实现
		~BinarySearchTree()
		{
			_Destory(_root);//销毁二叉搜索树
			_root = nullptr;//并将_root置空,防止越界访问
		}
		void _Destory(PNode& root)
		{
			if (root == nullptr)//空树直接返回
				return;
			_Destory(root->_left);//销毁左子树
			_Destory(root->_right);//销毁右子树
			delete root;
		}
插入
常规实现
按照二叉搜索树的性质对元素进行插入
 通过指针cur遍历二叉搜索树,pcur指针保存cur的父节点指针
bool Insert(const K& data)
		{
			if (nullptr == _root)//判断是否为空树
			{
				_root = new Node(data);
				return true;
			}
			//通过cur遍历二叉搜索树,cur最后指向的位置及为data插入位置
			//pcur保存cur的父节点,最后通过pcur链接新节点
			//_root及是整颗树的根节点,无父节点,先将pcur置空
			PNode pcur = nullptr;
			PNode cur = _root;
			//当cur == nullptr时,找到目标位置
			while (cur)
			{
				//保存cur当前位置
				pcur = cur;
				K& tmp = cur->_key;
				
				//进行数值判断
				if (data > tmp)
					cur = cur->_right;
				else if (data < tmp)
					cur = cur->_left;
				//相等时,插入失败
				else
					return false;
			}
			//虽然将cur的父节点传回,但不知为该节点的左子树还是右子树
			if (data > pcur->_key)
				pcur->_right = new Node(data);
			else
				pcur->_left = new Node(data);
			return true;
		}
递归实现
如果为空树,则new一个新节点直接赋值给_root
 非空树时,按照性质进行插入
- 插入值与节点值相同时,插入节点失败
- 插入值大于节点值时,将节点插入右子树
- 插入值小于节点值时,将节点插入左子树
 重复此操作直至寻到合适位置,将位置传给_InsertR()
传指针时需注意传递的为引用,通过引用十分方便的将父节点与子节点链接了起来
		bool InsertR(const K& data)
		{
			//递归插入需传递位置
			//对函数进行进一步封装
			return _InsertR(_root, data);
		}
		
		bool _InsertR(PNode& root, const K& data)
		{
			//	root 为空存在两种情况
			//其一:树为空树,new一个新节点并链接
			//其二:root当前位置为插入节点的目标所在
			//因root实际上传的为引用
			//对root的改变直接改变父节点中的指针
			//十分方便的实现了节点间的链接
			if (root == nullptr)
			{
				root = new Node(data);
				return true;
			}
			//判断data值的位置
			else if (data > root->_key)
				return _InsertR(root->_right, data);
			else
				return _InsertR(root->_left, data);
			return false;
		}
删除
二叉搜索树的删除是十分复杂的
 二叉搜索树具有独特的结构,删除结点的同时,也要保持树独特结构的保存
 当删除一个节点时,可以分成一下四种情况
- 要删除的结点无孩子结点
- 要删除的结点只有左孩子结点
- 要删除的结点只有右孩子结点
- 要删除的结点有左、右孩子结点
第一种情况可以和第二种或者第三种合并
 实际分类起始为三种
- 要删除的结点只有右孩子结点 直接删除,将父节点链接右孩子节点
- 要删除的结点只有左孩子结点 直接删除,将父节点链接左孩子节点
- 要删除的结点有左、右孩子结点 较为复杂,后文讨论
我们可以将节点的删除分为两个部分
- 找到节点
- 删除节点
非递归实现
找节点的过程与插入节点的过程相似
		bool Erase(const K& data)
		{
			if (_root == nullptr)
				return false;
			PNode pcur = nullptr;
			PNode cur = _root;
			while (cur)
			{
				K& tmp = cur->_key;
				if (data > tmp)
				{
					pcur = cur;
					cur = cur->_right;
				}
				else if (data < tmp)
				{
					pcur = cur;
					cur = cur->_left;
				}
				else
				{
					//找到节点了
				}
			}
			return false;
		}
查找完成后
 cur指针指向的为目标节点
 pcur指针指向的为目标节点的父节点
第二步删除节点
 据上文讨论删除节点可分三种情况
第一种要删除的结点只有右孩子结点 
 直接删除,将父节点链接右孩子节点
但也存在一些特殊情况,一般情况下pcur指向的是cur的父节点
 但当cur指向的是根节点时,根节点无父节点
 此时据查找函数可知,此时pcur为nullptr
 
		//该节点的左孩子为空
		if (cur->_left == nullptr)
		{
			//cur为根节点的特殊情况
			if (cur == _root)
			{
				_root = cur->_right;
			}
			else
			{	
				//判断cur是pcur的左子树还是右子树
				if (pcur->_left == cur)
					pcur->_left = cur->_right;
				else
					pcur->_right = cur->_right;
			}
				//删除cur节点,并将指针置空
				delete cur;
				cur = nullptr;
			}
第二种要删除的结点只有左孩子结点 
 直接删除,将父节点链接左孩子节点
 整体上与第一种类似
		//该节点的右孩子为空
		else if (cur->_right == nullptr)
		{
			//cur为根节点的特殊情况
			if (cur == _root)
			{
				_root = cur->_left;
			}
			else
			{
				//判断cur是pcur的左子树还是右子树
				if (pcur->_left == cur)
					pcur->_left = cur->_left;
				else
					pcur->_right = cur->_left;
			}
				//删除cur节点,并将指针置空
				delete cur;
				cur = nullptr;
		}
第三种情况要删除的结点有左、右孩子结点
 这种情况是最复杂的
 删除该节点且要保证,二叉搜索树的结构不被破坏
 如
 
 删除这个节点且要保证结构不破坏
 该如何实现呢?
 该节点被删除后,还有两颗子树需要处理
 比较好的方案便是补上该节点的位置
 补上该位置的节点有两点要求
- 要大于左节点的值
- 要小于右节点的值
有两种方案均满足需求
- 取右子树的最小节点
- 取左子树的最大节点
本文采取第一种方案
 取右子树的最小节点
 因右子树中所有元素均大于根节点
 左子树中所有元素均小于根节点
 右子树中的最小节点能很好的解决这个问题
此问题转化为 取右子树中的最小值
 根据平衡二叉树的性质 左子树的值小于根节点
 最小的节点一定是位于树的最左侧的
使用parent标记右子树中最小节点的父节点
 使用min 标记右子树中最小节点
		else
		{
			//查找cur右子树的最小子节点
			PNode parent = cur;
			PNode min = cur->_right;
			//min->left 等于nullptr时 min已经为最小节点
			while (min->_left != nullptr)
			{
				parent = min;
				min = min->_left;
			}
			
			//交换 cur 的值与 min 的值
			std::swap(min->_key, cur->_key);
			//min是parent的左孩子时
			if (parent->_left == min) 
			parent->_left = min->_right;
			//min是parent的右孩子
			//min是parent的右孩子,只有一种情况
			//即右子树只有一个节点
			//parent指向cur min指向cur->_right;
			else
			parent->_right = min->_right;
			delete min;
			return true;
		}
非递归删除的完整代码
		bool Erase(const K& data)
		{
			if (_root == nullptr)
				return false;
			PNode pcur = nullptr;
			PNode cur = _root;
			while (cur)
			{
				K& tmp = cur->_key;
				if (data > tmp)
				{
					pcur = cur;
					cur = cur->_right;
				}
				else if (data < tmp)
				{
					pcur = cur;
					cur = cur->_left;
				}
				else
				{
					if (cur->_left == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_right;
						}
						else
						{
							if (pcur->_left == cur)
								pcur->_left = cur->_right;
							else
								pcur->_right = cur->_right;
						}
						delete cur;
						cur = nullptr;
					}
					else if (cur->_right == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (pcur->_left == cur)
								pcur->_left = cur->_left;
							else
								pcur->_right = cur->_left;
						}
						delete cur;
						cur = nullptr;
					}
					else
					{
						//查找cur右子树的最小子节点
						PNode parent = cur;
						PNode min = cur->_right;
						while (min->_left != nullptr)
						{
							parent = min;
							min = min->_left;
						}
						std::swap(min->_key, cur->_key);
						if (parent->_left == min) 
						parent->_left = min->_right;
						else 
						parent->_right = min->_right;
						delete min;
						return true;
					}
				}
			}
			return false;
		}
递归删除
递归删除的的思路如下
 使用root传递需要删除元素所在树,同归递归缩小目标范围
 通过data传递元素值
当目标元素小于根节点的值时,去左子树搜索
 当目标元素大于根节点的值时,去右子树搜索
如果查到空树也没有找到该元素,返回false
 查到该元素时,具体的删除思路与非递归一致
 使用右子树的最小值或者左子树的最大值
 但也存在一些差异,在代码中具体说明
		bool EraseR(const K& data)
		{
			//递归删除,需要传递位置
			//对删除函数进行进一步封装
			return _EraseR(_root,data);
		}
		bool _EraseR(PNode& root, const K& data)
		{
			//空树,查找失败
			if (root == nullptr)
				return false;
			//元素大于节点值,去右子树中查找
			if (data > root->_key)
				return _EraseR(root->_right,data);
			//元素小于节点值,去左子树中查找
			else if (data < root->_key)
				return _EraseR(root->_left,data);
			//找到目标元素
			else
			{
				//元素的三种情况
				//该元素没有左孩子
				PNode tmp = root;
				if (root->_left == nullptr)
				{
					root = root->_right;
				}
				//该元素没有右孩子
				else if (root->_right == nullptr)
				{
					root = root->_left;
				}
				//该元素存在左孩子与右孩子
				else
				{	
					PNode min = root->_right;
					PNode parent = root;
					while (min->_left)
					{
						parent = min;
						min = min->_left;
					}
					std::swap(min->_key, root->_key);
					//上文代码与非递归相同
					//一下是两种删除节点的方法
					//第一种与非递归相同
					/*if (parent->_left == min)
						parent->_left = min->_right;
					else 
						parent->_right = min->_right;
					delete min;
					return true;*/
					//第二章起始为递归调用了_EraseR
					//被用以与目标元素替换的元素,他的子树数量一定小于等于1
					//调用删除代码删除时,不会走到此处
					return _EraseR(root->_right, data);
				}
			}
		}
查找
查找的具体思考,起始在删除与插入均均有使用
- 树为空树,查找失败,返回false
- 元素大于根节点,向右子树中查询
- 元素小于根节点,向左子树中查询
- 找到目标元素,返回true
非递归实现
		bool Find(const K& data)
		{
			PNode cur = _root;
			while (cur)
			{
				//元素与节点值相同,查询成功
				if (cur->_key == data)
					return true;
				//元素大于节点值,去右子树中查询
				else if (data > cur->_key)
					cur = cur->_right;
				//元素小于节点值,去左子树中查询
				else
					cur = cur->_left;
			}
			return false;
		}
递归实现
		bool FindR(const K& data)
		{
			return _FindR(_root, data);
		}
		bool _FindR(PNode root, const K& data)
		{
			//空树查找失败
			if (root == nullptr) 
				return false;
			//查找成功
			if (root->_key == data) 
				return true;
			//元素大于节点值,右子树中查询
			else if (root->_key < data)
				return _FindR(root->_right, data);
			//元素小于节点值,左子树中查询
			else
				return _FindR(root->_left, data);
		}
中序遍历
二叉树的中序遍历,得到的元素是一个升序
二叉搜索树中所有的非空节点均满足一下性质
 左节点非空时,一定小于该节点
 右节点非空时,一定大于该节点
当对二叉搜索树进行中序遍历时,依照中序遍历的顺序
- 左子树
- 根节点
- 右子树
根据二叉搜索树的性质可知。
 左子树中所有元素小于根节点,右子树中所有节点大于根节点
 如果按照先遍历左子树,再遍历根节点,最后遍历右子树
 那么此时根节点的位置的左侧均小于根节点,右侧均大于根节点
 
及为将树中元素按照升序排列时,根节点中元素应在的位置的位置
当遍历左子树与右子树时,也按照左子树,根节点,右子树的顺序遍历时
 所有元素均处于升序数组中的正确位置
 及中序遍历二叉搜索树时,遍历出的顺序为升序
递归实现
	void InOrder()
	{
		_InOrder(_root);
	}
	void _InOrder(PNode root)
	{
		if (root == nullptr)
		 return;
		_InOrder(root->_left);
		std::cout << root->_key << " ";
		_InOrder(root->_right);
	}
代码总览
#pragma once
#include<iostream>
template<class K>
struct BSNode
{
	BSNode(const K& data)
		:_left(nullptr)
		, _right(nullptr)
		, _key(data)
	{}
	BSNode<K>* _left;
	BSNode<K>* _right;
	K _key;
};
template<class K>
class BinarySearchTree
{
	typedef BSNode<K> Node;
	typedef Node* PNode;
	public:
		~BinarySearchTree()
		{
			_Destory(_root);
			_root = nullptr;
		}
		BinarySearchTree()
		{}
		BinarySearchTree(const BinarySearchTree<K>& t)
		{
			_root = _Copy(t._root);
		}
		BinarySearchTree<K>& operator=(BinarySearchTree<K> t)
		{
			std::swap(_root, t._root);
			return *this;
		}
		bool Insert(const K& data)
		{
			if (nullptr == _root)
			{
				_root = new Node(data);
				return true;
			}
			PNode pcur = nullptr;
			PNode cur = _root;
			while (cur)
			{
				pcur = cur;
				K& tmp = cur->_key;
				if (data > tmp)
					cur = cur->_right;
				else if (data < tmp)
					cur = cur->_left;
				else
					return false;
			}
			if (data > pcur->_key)
				pcur->_right = new Node(data);
			else
				pcur->_left = new Node(data);
			return true;
		}
		bool InsertR(const K& data)
		{
			return _InsertR(_root, data);
		}
		void InOrder()
		{
			_InOrder(_root);
		}
		bool Erase(const K& data)
		{
			if (_root == nullptr)
				return false;
			PNode pcur = nullptr;
			PNode cur = _root;
			while (cur)
			{
				K& tmp = cur->_key;
				if (data > tmp)
				{
					pcur = cur;
					cur = cur->_right;
				}
				else if (data < tmp)
				{
					pcur = cur;
					cur = cur->_left;
				}
				else
				{
					if (cur->_left == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_right;
						}
						else
						{
							if (pcur->_left == cur)
								pcur->_left = cur->_right;
							else
								pcur->_right = cur->_right;
						}
						delete cur;
						cur = nullptr;
					}
					else if (cur->_right == nullptr)
					{
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (pcur->_left == cur)
								pcur->_left = cur->_left;
							else
								pcur->_right = cur->_left;
						}
						delete cur;
						cur = nullptr;
					}
					else
					{
						//查找cur右子树的最小子节点
						PNode parent = cur;
						PNode min = cur->_right;
						while (min->_left != nullptr)
						{
							parent = min;
							min = min->_left;
						}
						std::swap(min->_key, cur->_key);
						if (parent->_left == min) parent->_left = min->_right;
						else parent->_right = min->_right;
						delete min;
						return true;
					}
				}
			}
			return false;
		}
		bool EraseR(const K& data)
		{
			return _EraseR(_root,data);
		}
		bool Find(const K& data)
		{
			PNode cur = _root;
			while (cur)
			{
				if (cur->_key == data)
					return true;
				else if (data > cur->_key)
					cur = cur->_right;
				else
					cur = cur->_left;
			}
			return false;
		}
		bool FindR(const K& data)
		{
			return _FindR(_root, data);
		}
		void Destory()
		{
			_Destory(_root);
		}
		private:
			bool _EraseR(PNode& root, const K& data)
			{
				if (root == nullptr)
					return false;
				if (data > root->_key)
					return _EraseR(root->_right,data);
				else if (data < root->_key)
					return _EraseR(root->_left,data);
				else
				{
					PNode tmp = root;
					if (root->_left == nullptr)
					{
						root = root->_right;
					}
					else if (root->_right == nullptr)
					{
						root = root->_left;
					}
					else
					{	
						PNode min = root->_right;
						PNode parent = root;
						while (min->_left)
						{
							parent = min;
							min = min->_left;
						}
						std::swap(min->_key, root->_key);
						/*if (parent->_left == min)
							parent->_left = min->_right;
						else 
							parent->_right = min->_right;
						delete min;
						return true;*/
						return _EraseR(root->_right, data);
					}
				}
			}
			bool _InsertR(PNode& root, const K& data)
			{
				if (root == nullptr)
				{
					root = new Node(data);
					return true;
				}
				else if (data > root->_key)
					return _InsertR(root->_right, data);
				else
					return _InsertR(root->_left, data);
				return false;
			}
			bool _FindR(PNode root, const K& data)
			{
				if (root == nullptr) return false;
				if (root->_key == data) return true;
				else if (root->_key < data)
					return _FindR(root->_right, data);
				else
					return _FindR(root->_left, data);
			}
			void _InOrder(PNode root)
			{
				if (root == nullptr) return;
				_InOrder(root->_left);
				std::cout << root->_key << " ";
				_InOrder(root->_right);
			}
			void _Destory(PNode& root)
			{
				if (root == nullptr)
					return;
				_Destory(root->_left);
				_Destory(root->_right);
				delete root;
			}
			PNode _Copy(PNode root)
			{
				if (root == nullptr)
					return nullptr;
				PNode CopyRoot = new Node(root->_key);
				CopyRoot->_left = _Copy(root->_left);
				CopyRoot->_right = _Copy(root->_right);
				return CopyRoot;
			}
			PNode _root = nullptr;
	};
二叉搜索树的应用
二叉树有两种模型
*K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到
 的值
 比如:给定一个单词,判断该单词是否拼写正确。具体方式如下:
- 以单词集合中的每个单词作为key,构建一棵二叉搜索树。
- 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。
KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。
 比如:英汉词典就是英文与中文的对应关系,即<word, Chinese>就构成一种键值对。具体方式如下:
- 以<单词, 中文含义>为键值对,构建一棵二叉搜索树。注意:二叉搜索树需要进行比较,键值对比较时只比较key。
- 查询英文单词时,只需给出英文单词就可以快速找到与其对应的中文含义。



















