C++|二叉搜索树

一、二叉搜索树的概念

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

  • 若它的左子树不为空,则左子树上所有节点的值小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根结点的值
  • 它的每一颗子树都是搜索二叉树,满足该三条规则。

可以简单的总结一下,整个左子树的值比根小,整个右子树的值比根大,且每一颗子树符合该规则

例如:                    二叉搜索树                                            非二叉搜索树,3的左子树大于根

 

二、二叉搜索树的实现

二叉搜索树的实现,首先得创建一个节点类,用来存放数据,接着再创建树的框架,用来管理节点的插入,查找,删除等操作 。

2.1节点创建

实现成模板,可以存放各种类型的数据,为了让节点与节点之间关联起来,所以有两个指针,_left,_right分别指向左右节点 ,_data则是存放具体值。构造函数则是初始化节点的内容

	template<class T>
	struct BSTnode
	{
		BSTnode(const T& data = T())//初始化节点内容
			:_left(nullptr)
			, _right(nullptr)
			, _data(data)
		{}
		BSTnode<T>* _left;
		BSTnode<T>* _right;
		T _data;
	};

2.2构造与拷贝构造

创建节点后,接着创建树,用来管理节点,首先就是实现构造函数对节点进行初识化,然后实现拷贝构造,拷贝构造需要将一颗树的所有节点值全拷贝过来,故可以采用递归的方式实现。

	template<class T>
	class BSTree
	{
		typedef BSTnode<T> Node;
		typedef Node* PNode;

	public:
		BSTree()
			:_Root(nullptr)
		{}
		BSTree(const BSTree<T>& t)
		{
			_Root = CopyNode(t._Root);
		}
		PNode CopyNode(PNode Root)
		{
			if (Root == nullptr)
			{
				return nullptr;
			}
			PNode node = new Node;//创建新结点,存放节点值

			node->_data = Root->_data;//拷贝节点值
			node->_left = CopyNode(Root->_left);//链接左节点
			node->_right = CopyNode(Root->_right);//链接右节点
			return node;
		}
        
	private:
		PNode _Root;//采用节点指针
	};

2.3插入(循环版本&&递归版本)

 接下来进行具体的管理节点,首先就是节点的插入,思想的实现可以分为两个步骤:

1.树为空,则直接新增节点,赋值给_Root 指针

2.树不为空,按二叉树的性质搜索查找插入位置,插入新节点

3.若出现相同的值则不插入

二叉搜索树需要不断的进行比较,最终插入,所以其实现可以用循环和递归实现,为了表示是否插入成功,所以使其需要返回值。

例如:插入新节点,16、0。根据二叉搜索树的性质,进行比较,插入 

 

循环版本

		bool Insert(const T& data)
		{
			//空树,新增节点
			if (_Root == nullptr)
			{
				_Root = new Node;
				_Root->_data = data;
				return true;
			}
			//不为空,进行比较
			PNode cur = _Root;
			PNode parent = nullptr;//存放cur的上一个位置
			while (cur)
			{
				parent = cur;
				if (data < cur->_data)//小于根节点,则往左子树
				{
					cur = cur->_left;
				}
				else if (data > cur->_data)//大于根节点,则往右子树
				{
					cur = cur->_right;
				}
				else//有相同值,返回假
				{
					return false;
				}
			}
            //循环结束,说明找到了要插入的位置,但cur为空
            //而parent是cur的上一个位置,所以用parent比较插入
			if (data < parent->_data)
			{
				PNode node = new Node;
				node->_data = data;
				parent->_left = node;
			}
			else if (data > parent->_data)
			{
				PNode node = new Node;
				node->_data = data;
				parent->_right = node;
			}
			return true;
		}

递归版本 

        bool Insert(const T& data)
		{
			
			return _Insert(_Root,data);
		}
		bool _Insert(PNode& Root,const T& data)
		{
            //为空,新增节点,直接返回,或者,不为空在最后插入节点,返回
			if (Root == nullptr)
			{
				//PNode node = new Node;
				//Root = node;
				//node->_data = data;
                Root = new Node(data);
				return true;
			}
			if (data < Root->_data)//小于根节点,往左子树
				return _Insert(Root->_left, data);
			else if (data > Root->_data)
				return _Insert(Root->_right, data);//大于根节点,往右子树
			else
				return false;
		}

2.4查找(循环版本&&递归版本)

同理,查找需要不断进行比较,依然可以通过循化和递归实现。 查找成功,返回当前位置指针,否则返回nullptr

循环版本

        PNode Find(const T& data)
		{
			//树空
			if (_Root == nullptr)
			{
				return nullptr;
			}
			//不为空
			PNode cur = _Root;
			while (cur)
			{

				if (data < cur->_data)
				{
					cur = cur->_left;
				}
				else if (data > cur->_data)
				{
					cur = cur->_right;
				}
				else
				{
					return cur;//找到,返回该位置指针
				}
			}
			return nullptr;
		}

 

递归版本

        PNode Find(const T& data)
		{
			return _Find(_Root,data);
		}
		PNode _Find(PNode Root,const T& data)
		{
			if (Root == nullptr)
			{
				return nullptr;
			}
			if (data < Root->_data)
			{
				return _Find(Root->_left, data);
			}
			else if (data > Root->_data)
			{
				return _Find(Root->_right, data);
			}
			else
			{
				return Root;
			}
		}

 

2.5删除(循环版本&&递归版本)

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

1.要删除的结点无孩子结点

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

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

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

①要删除的节点无孩子、只有左孩子、右孩子可以归类为一种情况。 都是让删除节点的父亲指向删除节点的孩子即可。

 

②要删除的节点有左右孩子可以归类为一种情况。

删除该节点时,其还有左右孩子,所以导致的问题是得重新排序链接。为了保持二叉搜索树的结构,可以采用替换删除法:找一个替换我的节点,交换值,转换删除他。那么其有两种删除方式。

                                            a.找删除结点的左子树的最大节点进行交换删除(左子树最右节点)

                                            b.找删除结点的右子树的最小节点进行交换删除(右子树最左节点)

解释:因为左子树中的最大节点比删除结点的左孩子大、右孩子小。右子树中的最小节点也比删除结点的左孩子大、右孩子小。那么交换删除,依然满足二叉搜索树的结构。

在代码实现中,就采用第二种,找右子树的最小节点。

  

循环版本

		bool Erase(const T& data)
		{
			if (_Root == nullptr)//树为空,返回fasle
				return false;
			PNode cur = _Root;
			PNode parent = _Root;//跟踪cur,始终保持为cur的父亲
			//查找要删除节点位置
			while (cur)
			{
				if (data < cur->_data)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (data > cur->_data)
				{
					parent = cur;
					cur = cur->_right;
				}
				else
					break;//找到跳出
			}
			//未找到,返回FALSE
			if (cur == nullptr)
				return false;

			//要删除的节点只有右孩子
			if (cur->_left == nullptr)
			{
                //要删除的节点是根节点,更新根节点,再删除
				if (cur == _Root)
				{
					_Root = cur->_right;
				}
				//判断该删除结点是父亲的左孩子还是右孩子
				if (data < parent->_data)//是父亲的左孩子
				{
					parent->_left = cur->_right;
				}
				else if(data > parent->_data)//是父亲的右孩子
				{
					parent->_right = cur->_right;
				}
				delete cur;

			}
			else if (cur->_right == nullptr)//要删除的结点只有左孩子
			{
                //要删除的节点是根节点,更新根节点,再删除
				if (cur == _Root)
				{
					_Root = cur->_left;
				}
				//判断该删除结点是父亲的左孩子还是右孩子
				if (data < parent->_data)//是父亲的左孩子
				{
					parent->_left = cur->_left;
				}
				else if(data > parent->_data)//是父亲的右孩子
				{
					parent->_right = cur->_left;
				}
				delete cur;

			}
			else//跟右子树的最左节点(即最小节点)进行交换,再删除
			{
			
				PNode pparent = cur;//跟踪parent
				parent = cur->_right;//先指向右子树的根节点


				//找右子树最左节点
				while (parent->_left)
				{

					pparent = parent;
					parent = parent->_left;

				}
				//交换,重新链接,删除
				cur->_data = parent->_data;
				if (cur == pparent)
				{
					pparent->_right = parent->_right;
				}
				else
				{
					pparent->_left = parent->_right;
				}
				delete parent;
			}
			return true;//删除成功,返回true

		}

 递归版本

        bool Erase(const T& data)
		{
			return _Erase(_Root, data);
		}
		bool _Erase(PNode& Root,const T& data)//Root采用引用,引用的是上一个Root->_left。目的是,当找到删除结点,其只有一个孩子或者无孩子,就可以让删除结点的父亲指向孩子,该父亲就是当前Root,引用的是上一个Root->_left。即PNode& Root= Root->_left
		{
			if (Root == nullptr)
			{
				return false;
			}
            //查找删除结点
			if (data < Root->_data)
			{
				_Erase(Root->_left, data);//小于查找节点,到左子树查找
			}
			else if (data > Root->_data)
			{
				_Erase(Root->_right, data);//大于查找节点,到右子树查找
			}
			else//找到删除结点,判断其是否有孩子,进行交换删除
			{
                
                PNode cur = Root;
				//要删除的节点只有右孩子
				if (Root->_left == nullptr)
				{				
					Root = Root->_right;//链接右孩子
				}
				else if (Root->_right == nullptr)//要删除的节点只有左孩子
				{
	
					Root = Root->_left;//链接左孩子
				}
				else//要删除的节点有左右孩子,采用替换法删除
				{
                    
					cur = Root->_right;
                    //寻找最左节点
					while (cur->_left)
					{
						cur = cur->_left;
					}
                    //交换
					swap(Root->_data,cur->_data);
                    
					return _Erase(Root->_right, data);//转换成在删除结点的右子树中查找交换后要删除的节点,因为交换后的删除的节点其要么只有一个孩子要么没有孩子
				}
				delete cur;
                cur = nullptr;
                return true;
			}
			
		}

2.6析构

        void Delete(PNode Root)//后序递归删除
		{
			if (Root == nullptr)
				return;
			Delete(Root->_left);
			Delete(Root->_right);
			delete Root;
		}

		~BSTree()
		{
			if (_Root == nullptr)
				delete _Root;
			Delete(_Root);
		}

2.7总代码(循环版本&&递归版本)

循环版本:

//BSTeee-key.h
#include <iostream>
using namespace std;

namespace bit
{
	template<class T>
	struct BSTnode
	{
		BSTnode(const T& data = T())
			:_left(nullptr)
			, _right(nullptr)
			, _data(data)
		{}
		BSTnode<T>* _left;
		BSTnode<T>* _right;
		T _data;
	};

	template<class T>
	class BSTree
	{
		typedef BSTnode<T> Node;
		typedef Node* PNode;

	public:
		BSTree()
			:_Root(nullptr)
		{}
		BSTree(const BSTree<T>& t)
		{
			_Root = CopyNode(t._Root);
		}
		PNode CopyNode(PNode Root)
		{
			if (Root == nullptr)
			{
				return nullptr;
			}
			PNode node = new Node;

			node->_data = Root->_data;
			node->_left = CopyNode(Root->_left);
			node->_right = CopyNode(Root->_right);
			return node;
		}

		//插入
		bool Insert(const T& data)
		{
			//空树
			if (_Root == nullptr)
			{
				_Root = new Node;
				_Root->_data = data;
				return true;
			}
			//不为空
			PNode cur = _Root;
			PNode parent = nullptr;
			while (cur)
			{
				parent = cur;
				if (data < cur->_data)
				{
					cur = cur->_left;
				}
				else if (data > cur->_data)
				{
					cur = cur->_right;
				}
				else
				{
					return false;
				}
			}
			if (data < parent->_data)
			{
				PNode node = new Node;
				node->_data = data;
				parent->_left = node;
			}
			else if (data > parent->_data)
			{
				PNode node = new Node;
				node->_data = data;
				parent->_right = node;
			}
			return true;
		}

		//查找
		PNode Find(const T& data)
		{
			//树空
			if (_Root == nullptr)
			{
				return nullptr;
			}
			//不为空
			PNode cur = _Root;
			while (cur)
			{

				if (data < cur->_data)
				{
					cur = cur->_left;
				}
				else if (data > cur->_data)
				{
					cur = cur->_right;
				}
				else
				{
					return cur;
				}
			}
			return nullptr;
		}

		//删除--替换删除法
		bool Erase(const T& data)
		{
			if (_Root == nullptr)//树为空,返回fasle
				return false;
			PNode cur = _Root;
			PNode parent = _Root;//跟踪cur,始终保持为cur的父亲
			//查找要删除节点位置
			while (cur)
			{
				if (data < cur->_data)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (data > cur->_data)
				{
					parent = cur;
					cur = cur->_right;
				}
				else
					break;//找到跳出
			}
			//未找到,返回FALSE
			if (cur == nullptr)
				return false;

			//要删除的节点只有右孩子
			if (cur->_left == nullptr)
			{
                //要删除的节点是根节点,更新根节点,再删除
				if (cur == _Root)
				{
					_Root = cur->_right;
				}
				//判断该删除结点是父亲的左孩子还是右孩子
				if (data < parent->_data)//是父亲的左孩子
				{
					parent->_left = cur->_right;
				}
				else if(data > parent->_data)//是父亲的右孩子
				{
					parent->_right = cur->_right;
				}
				delete cur;

			}
			else if (cur->_right == nullptr)//要删除的结点只有左孩子
			{
                //要删除的节点是根节点,更新根节点,再删除
				if (cur == _Root)
				{
					_Root = cur->_left;
				}
				//判断该删除结点是父亲的左孩子还是右孩子
				if (data < parent->_data)//是父亲的左孩子
				{
					parent->_left = cur->_left;
				}
				else if(data > parent->_data)//是父亲的右孩子
				{
					parent->_right = cur->_left;
				}
				delete cur;

			}
			else//跟右子树的最左节点(即最小节点)进行交换,再删除
			{
			
				PNode pparent = cur;//跟踪parent
				parent = cur->_right;//先指向右子树的根节点


				//找右子树最左节点
				while (parent->_left)
				{

					pparent = parent;
					parent = parent->_left;

				}
				//交换,重新链接,删除
				cur->_data = parent->_data;
				if (cur == pparent)
				{
					pparent->_right = parent->_right;
				}
				else
				{
					pparent->_left = parent->_right;
				}
				delete parent;
			}
			return true;//删除成功,返回true

		}

		void Delete(PNode Root)
		{
			if (Root == nullptr)
				return;
			Delete(Root->_left);
			Delete(Root->_right);
			delete Root;
		}

		~BSTree()
		{
			if (_Root == nullptr)
				delete _Root;
			Delete(_Root);
		}
		void _InOrder(PNode Root)
		{
			if (Root == nullptr)
				return;

			_InOrder(Root->_left);
			cout << Root->_data << " ";
			_InOrder(Root->_right);
		}

		void InOrder()
		{
			_InOrder(_Root);
			cout << endl;
		}

	private:
		PNode _Root;//采用节点指针
	};


	void BSTreetest()
	{
		BSTree<int> t;
		int a[] = { 8,3,1,10,6,4,7,14,13 };
		for (int i = 0; i < 9; i++)
		{
			t.Insert(a[i]);
		}

		t.Erase(6);
		BSTree<int> ts(t);
		ts.InOrder();
	}

}

测试:

test.cpp
#include "BSTeee-key.h"
int main()
{
	bit::BSTreetest();
	return 0;
}

输出结果: 

 

递归版本:

//BSTeee-key2.h
#include <iostream>
using namespace std;

namespace bit
{
	template<class T>
	struct BSTnode
	{
		BSTnode(const T& data = T())
			:_left(nullptr)
			, _right(nullptr)
			, _data(data)
		{}
		BSTnode<T>* _left;
		BSTnode<T>* _right;
		T _data;
	};

	template<class T>
	class BSTree
	{
		typedef BSTnode<T> Node;
		typedef Node* PNode;

	public:
		BSTree()
			:_Root(nullptr)
		{}
		BSTree(const BSTree<T>& t)
		{
			_Root = CopyNode(t._Root);
		}
		PNode CopyNode(PNode Root)
		{
			if (Root == nullptr)
			{
				return nullptr;
			}
			PNode node = new Node;
			node->_data = Root->_data;
			node->_left = CopyNode(Root->_left);
			node->_right = CopyNode(Root->_right);
			return node;
		}

		//插入
		bool Insert(const T& data)
		{
			
			return _Insert(_Root,data);
		}
		bool _Insert(PNode& Root,const T& data)
		{
			if (Root == nullptr)
			{
				PNode node = new Node;
				Root = node;
				node->_data = data;
				return true;
			}
			if (data < Root->_data)
				return _Insert(Root->_left, data);
			else if (data > Root->_data)
				return _Insert(Root->_right, data);
			else
				return false;
		}

		//查找
		PNode Find(const T& data)
		{
			return _Find(_Root,data);
		}
		PNode _Find(PNode Root,const T& data)
		{
			if (Root == nullptr)
			{
				return nullptr;
			}
			if (data < Root->_data)
			{
				return _Find(Root->_left, data);
			}
			else if (data > Root->_data)
			{
				return _Find(Root->_right, data);
			}
			else
			{
				return Root;
			}
		}
		//删除--替换删除法
		bool Erase(const T& data)
		{
			return _Erase(_Root, data);
		}
		bool _Erase(PNode& Root,const T& data)
		{
			if (Root == nullptr)
			{
				return false;
			}

			if (data < Root->_data)
			{
				_Erase(Root->_left, data);
			}
			else if (data > Root->_data)
			{
				_Erase(Root->_right, data);
			}
			else
			{
				
				PNode cur = Root;
				//要删除的节点只有右孩子
				if (Root->_left == nullptr)
				{
					Root = Root->_right;//链接右孩子
				}
				else if (Root->_right == nullptr)//要删除的节点只有左孩子
				{

					Root = Root->_left;//链接左孩子
				}
				else//要删除的节点有左右孩子,采用替换法删除
				{

					cur = Root->_right;
					//寻找最左节点
					while (cur->_left)
					{
						cur = cur->_left;
					}
					//交换
					swap(Root->_data, cur->_data);

					return _Erase(Root->_right, data);//转换成在删除结点的右子树中查找交换后要删除的节点,因为交换后的删除的节点其要么只有一个孩子要么没有孩子
				}
				delete cur;
				cur = nullptr;
				return true;
				
			}

		}
		void Delete(PNode Root)
		{
			if (Root == nullptr)
				return;
			Delete(Root->_left);
			Delete(Root->_right);
			delete Root;
		}

		~BSTree()
		{
			if (_Root == nullptr)
				delete _Root;
			Delete(_Root);
		}
		void _InOrder(PNode Root)
		{
			if (Root == nullptr)
				return;

			_InOrder(Root->_left);
			cout << Root->_data << " ";
			_InOrder(Root->_right);
		}

		void InOrder()
		{
			_InOrder(_Root);
			cout << endl;
		}

	private:
		PNode _Root;//采用节点指针
	};


	void BSTreetest()
	{
		BSTree<int> t;
		int a[] = { 8,3,1,10,6,4,7,14,13 };
		for (int i = 0; i < 9; i++)
		{
			t.Insert(a[i]);
		}
		BSTnode<int>* p = t.Find(5);
		if (p == nullptr)
		{
			t.Insert(5);
		}

		t.Erase(3);
        t.Erase(6);
		BSTree<int> ts(t);
		ts.InOrder();
	}
}

 测试:

//test.cpp
#include "BSTeee-key2.h"
int main()
{
	bit::BSTreetest();
	return 0;
}

输出结果: 

三、二叉搜索树的应用

3.1 k模型 && kv模型

K模型:k模型即只有key作为关键码,结构中只需要存储key即可,关键码即为需要搜索到的值。就跟上面的代码实现一样。

比如:查询某人是否买了机票。就可以建立采用搜索二叉树,在二叉搜索树中查询该人是否存在,存在,已买,否则,未买。

kv模型:每一个关键码key,都有与之对应的值value,即<key,value>的键值对。

比如:英汉词典就是英文与中文的对应关系,通过英文了以快速找到与其对应的中文,英文单词与其对应的中文(apple,"苹果")就构成一种键值对;

3.2 KV模型的实现

对于kv模型的实现没有什么太大变化,就是在加一个模板参数,在插入节点时,给另一个模板参数也进行赋值即可。进行比较时,还是按第一个参数来比较,即按key来比较,跟value无关。

//BSTree.h
#include <iostream>
#include <string>
using namespace std;

namespace bit
{
	template<class K,class V>
	struct BSTnode
	{
		BSTnode(const K& key = K(),const V& value= V())
			:_left(nullptr)
			, _right(nullptr)
			, _key(key)
			,_value(value)
		{}
		BSTnode<K, V>* _left;
		BSTnode<K, V>* _right;
		K _key;
		V _value;
	};

	template<class K, class V>
	class BSTree
	{
	public:
		typedef BSTnode<K, V> Node;
		typedef Node* PNode;
	public:
		BSTree()
			:_Root(nullptr)
		{}

		//插入
		bool Insert(const K& key, const V& value)
		{
			//空树
			if (_Root == nullptr)
			{
				_Root = new Node;
				_Root->_key = key;
				_Root->_value = value;
				return true;
			}
			//不为空
			PNode cur = _Root;
			PNode parent = nullptr;
			while (cur)
			{
				parent = cur;
				if (key < cur->_key)
				{
					cur = cur->_left;
				}
				else if (key > cur->_key)
				{
					cur = cur->_right;
				}
				else
				{
					return false;
				}
			}
			if (key < parent->_key)
			{
				PNode node = new Node;
				node->_key = key;
				node->_value = value;
				parent->_left = node;
			}
			else if (key > parent->_key)
			{
				PNode node = new Node;
				node->_key = key;
				node->_value = value;
				node->_value = value;
				parent->_right = node;
			}
			return true;
		}

		//查找
		PNode Find(const K& key)
		{
			//树空
			if (_Root == nullptr)
			{
				return nullptr;
			}
			//不为空
			PNode cur = _Root;
			while (cur)
			{

				if (key < cur->_key)
				{
					cur = cur->_left;
				}
				else if (key > cur->_key)
				{
					cur = cur->_right;
				}
				else
				{
					return cur;
				}
			}
			return nullptr;
		}

		//删除--替换删除法
		bool Erase(const K& key)
		{
			if (_Root == nullptr)
				return false;
			PNode cur = _Root;
			PNode parent = nullptr;
			//查找要删除节点位置
			while (cur)
			{
				if (key < cur->_key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (key > cur->_key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else
					break;
			}
			//未找到,返回FALSE
			if (cur == nullptr)
				return false;

			//要删除的节点只有右孩子
			if (cur->_left == nullptr)
			{
				//要删除的节点是根节点,更新根节点,再删除
				if (cur == _Root)
				{
					_Root = cur->_right;
				}
				//判断该删除结点是父亲的左孩子还是右孩子
				if (key < parent->_key)//是父亲的左孩子
				{
					parent->_left = cur->_right;
					delete cur;
				}
				else if(key > parent->_key)//是父亲的右孩子
				{
					parent->_right = cur->_right;
					delete cur;
				}
			}
			else if (cur->_right == nullptr)//要删除的节点只有右孩子
			{
				//要删除的节点是根节点,更新根节点,再删除
				if (cur == _Root)
				{
					_Root = cur->_right;
				}
				//判断该删除结点是父亲的左孩子还是右孩子
				if (key < parent->_key)//是父亲的左孩子
				{
					parent->_left = cur->_left;
					delete cur;
				}
				else if(key > parent->_key)//是父亲的右孩子
				{
					parent->_right = cur->_left;
					delete cur;
				}
			}
			else//跟右子树的最左节点(即最小节点)进行交换,再删除
			{
				PNode pparent = cur;
				parent = cur->_right;


				//找最左节点
				while (parent->_left)
				{

					pparent = parent;
					parent = parent->_left;

				}
				//交换删除
				cur->_key = parent->_key;
				if (cur == pparent)
				{
					pparent->_right = parent->_right;
				}
				else
				{
					pparent->_left = parent->_right;
				}
				delete parent;
			}
			return true;

		}
		

		~BSTree()
		{
			if (_Root == nullptr)
				delete _Root;
			Delete(_Root);
		}


		void InOrder()
		{
			_InOrder(_Root);
			cout << endl;
		}

	private:
		void Delete(PNode Root)
		{
			if (Root == nullptr)
				return;
			Delete(Root->_left);
			Delete(Root->_right);
			delete Root;
		}
		void _InOrder(PNode Root)
		{
			if (Root == nullptr)
				return;

			_InOrder(Root->_left);
			cout << Root->_key << ":" << Root->_value << endl;;
			_InOrder(Root->_right);
		}
		PNode _Root;//采用节点指针
	};


	/*void BSTreetest()
	{
		BSTree<int> t;
		t.Insert(1);
		t.Insert(28);
		t.Insert(3);
		t.Insert(4);
		t.InOrder();
	}*/

	//void BSTreetest2()
	//{
	//	BSTree<int, int> t;
	//	t.Insert(1, 1);
	//	t.Insert(1, 1);

	//	t.Insert(2, 1);
	//	t.Insert(3, 1);
	//	t.Insert(4, 1);
	//	t.Insert(5, 1);
	//	t.Insert(6, 1);
	//	t.Erase(3);
	//	t.InOrder();
	//}

	//查询单词
	//void BSTreetest3()
	//{
	//	BSTree<string, string> dict;
	//	//插入单词
	//	dict.Insert("string", "字符串");
	//	dict.Insert("binary", "二叉");
	//	dict.Insert("search", "搜索");
	//	dict.Insert("tree", "树");
	//	dict.Insert("sort", "排序");

	//	//查询单词是否在
	//	string str;
	//	while (cin >> str)
	//	{
	//		 BSTnode<string, string>* ret = dict.Find(str);
	//		 if(ret == nullptr)
	//		 {
	//			 cout << "单词拼写错误,词库中没有这个单词:" << str << endl;
	//		 }
	//		 else
	//		 {
	//			 cout << str << "中文翻译:" << ret->_value << endl;
	//		 }
	//	}

	//}

	//统计水果出现的次数
	void BSTreetest4()
	{
		string str[] = { "香蕉","苹果","荔枝","梨","苹果", "苹果", "西瓜","香蕉","香蕉","梨" };
		BSTree<string, int> countTree;
		for (const auto& s : str)
		{
			BSTnode<string, int>* ret = countTree.Find(s);
			if (ret == NULL)
			{
				countTree.Insert(s, 1);
			}
			else
			{
				ret->_value++;
			}
		}
		countTree.InOrder();
	}
}

 测试:

//test.cpp
#include "BSTeee.h"
int main()
{
	bit::BSTreetest4();
	return 0;
}

输出结果:

3.3二叉搜索树的性能分析 

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

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

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

 

最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为:log N

最差情况下,二叉搜索树退化为单支树,其平均比较次数为: N^2

所以问题就是,如果退化成单支树,二叉搜索树的性能就失去了。为了解决该问题,大佬们发明了了AVL树和红黑树,待后续章节进行学习。

end~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/625345.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AVL树、红黑树

数据结构、算法总述&#xff1a;数据结构/算法 C/C-CSDN博客 AVL树 定义 空二叉树是一个 AVL 树如果 T 是一棵 AVL 树&#xff0c;那么其左右子树也是 AVL 树&#xff0c;并且 &#xff0c;h 是其左右子树的高度树高为 平衡因子&#xff1a;右子树高度 - 左子树高度 创建节点…

图片标签 以及 常见的图片的格式

1.图片的基本使用 2.图片的常见格式 3.bmp格式

易我分区大师18.5发布上线:全方位提升您的磁盘管理体验

近期&#xff0c;易我分区大师18.5版本正式发布上线&#xff01; 新版本在原有基础上进行了升级和优化&#xff0c;不仅继承了前版本的强大功能&#xff0c;还新增了C盘数据迁移、清除Windows PIN码以及蓝屏问题助手等实用功能&#xff0c;帮助用户更轻松、更高效地管理电脑磁…

HTML的使用(中)

文章目录 前言一、HTML表单是什么&#xff1f;二、HTML表单的使用 &#xff08;1&#xff09;<form>...</form>表单标记&#xff08;2&#xff09;<input>表单输入标记总结 前言 在许多网页平台上浏览&#xff0c;大多逃不了登录账号。此时在网页中填写的用户…

数据库SQL编写规范-SQL书写规范整理(SQL语句书写规范全解-Word原件)

编写本文档的目的是保证在开发过程中产出高效、格式统一、易阅读、易维护的SQL代码。 1 编写目 2 SQL书写规范 3 SQL编写原则 软件全套精华资料包清单部分文件列表&#xff1a; 工作安排任务书&#xff0c;可行性分析报告&#xff0c;立项申请审批表&#xff0c;产品需求规格说…

吴恩达深度学习笔记:优化算法 (Optimization algorithms)2.3-2.5

目录 第二门课: 改善深层神经网络&#xff1a;超参数调试、正 则 化 以 及 优 化 (Improving Deep Neural Networks:Hyperparameter tuning, Regularization and Optimization)第二周&#xff1a;优化算法 (Optimization algorithms)2.3 指数加权平均数&#xff08;Exponential…

unity 学习笔记

一、 事件顺序 gameObjet Instantiate gameObjet.自定义函数 gameObjet.Start 二、预设体使用 例子&#xff1a;Button 点击创建 预设体 BagPanel

【MIT6.S081】Lab7: Multithreading(详细解答版)

实验内容网址:https://xv6.dgs.zone/labs/requirements/lab7.html 本实验的代码分支:https://gitee.com/dragonlalala/xv6-labs-2020/tree/thread2/ Uthread: switching between threads 关键点:线程切换、swtch 思路: 本实验完成的任务为用户级线程系统设计上下文切换机制…

windows平台Visual Studio2022编译libuvc调试usb摄像头

一、下载libuv源码&#xff0c;源码地址&#xff1a;libuvc/libuvc: a cross-platform library for USB video devices (github.com) 二、新建vs工程&#xff0c;将libuvc源码中的include和src目录下的文件拷贝到工程中。 1.include源码修改 ①libuvc头文件修改 将 #includ…

自动删除 PC 端微信缓存数据,包括从所有聊天中自动下载的大量文件、视频、图片等数据内容,解放你的空间。

Clean My PC Wechat 自动删除 PC 端微信自动下载的大量文件、视频、图片等数据内容&#xff0c;解放一年几十 G 的空间占用。 该工具不会删除文字的聊天记录&#xff0c;请放心使用。请给个 Star 吧&#xff0c;非常感谢&#xff01; 现已经支持 Windows 系统中的所有微信版本…

Java进阶11 IO流、功能流

Java进阶11 IO流-功能流 一、字符缓冲流 字符缓冲流在源代码中内置了字符数组&#xff0c;可以提高读写效率 1、构造方法 方法说明BufferedReader(new FileReader(文件路径))对传入的字符输入流进行包装BufferedWriter(new FileWriter(文件路径))对传入的字符输出流进行包装…

基于Springboot+Vue的Java项目-宠物商城网站系统开发实战(附演示视频+源码+LW)

大家好&#xff01;我是程序员一帆&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &am…

暗区突围TWITCH掉宝领测试资格后,steam激活显示是无效激活码

自《暗区突围》测试启动以来&#xff0c;吸引了大量玩家关注&#xff0c;特别是通过在Twitch平台上观看直播即可获得测试资格的活动&#xff0c;更是掀起了热潮。然而&#xff0c;部分玩家在成功获得激活码后&#xff0c;在Steam平台激活时遭遇了“无效激活码”的问题。本文将提…

勒索软件漏洞?在不支付赎金的情况下解密文件

概述 在上一篇文章中&#xff0c;笔者对BianLian勒索软件进行了研究剖析&#xff0c;并且尝试模拟构建了一款针对BianLian勒索软件的解密工具&#xff0c;研究分析过程中&#xff0c;笔者感觉构建勒索软件的解密工具还挺有成就感&#xff0c;因此&#xff0c;笔者准备再找一款…

排序-归并排序(merge sort)

归并排序&#xff08;Merge Sort&#xff09;是一种分而治之的算法&#xff0c;它将原始数组分成越来越小的子数组&#xff0c;直到每个子数组只有一个元素&#xff0c;然后将这些子数组两两合并&#xff0c;过程中保持排序状态&#xff0c;最终合并成一个完全有序的数组。归并…

windows和Linux卸载移动磁盘

文章目录 Linux卸载磁盘target is busy.window卸载磁盘打开事件查看器 Linux卸载磁盘target is busy. #查看有哪些进程访问挂载点 lsof /media/lei/repository/#杀死进程 pkill node window卸载磁盘 #提示 #该设备正在使用中. 请关闭可能使用该设备的所有程序或窗口,然后重试…

ZL-016D多通道小鼠主动跑轮系统主要研究动物生活节律

简单介绍&#xff1a; 多通道小鼠主动跑轮系统是由动物本身自发运动来推动跑轮转动。在这种构型中&#xff0c;笼内动物长期活动的信息&#xff0c;如跑轮转动方向、转数、累计总行程等&#xff0c;能够使用编码器进行长度计记录。此装置由转轮组件、笼体、以及转动方向速度传…

国产分布式数据库高可用故障检测实现

在分布式数据库架构下&#xff0c;当数据库节点异常时&#xff0c;数据库管理组件能够自动感知到异常并触发节点隔离或者自动切换&#xff0c;是数据库高可用容灾的基本能力。在节点服务器异常、网络异常或进程异常等场景下&#xff0c;各数据库产品本身已经具备了可靠的检测能…

前端连续发送同一个请求时,终止上一次请求

场景&#xff1a;几个tab页之间快速的切换&#xff08;tab页只是参数不同&#xff0c;下边的数据渲染给同一个data&#xff09;就会导致如果我在1,2,3&#xff0c;tab页按照顺序快速点击&#xff0c;发送三个请求&#xff0c;我想要展示的是3但是如果1或者2请求响应的时间比3长…

超简洁的todolist工具,电脑桌面高效计划管理软件

对于上班族来说&#xff0c;在电脑上使用一款高效计划管理软件至关重要。这样的工具不仅能帮助我们清晰地规划和追踪工作任务&#xff0c;还能有效提高工作效率&#xff0c;减少遗漏和延误。例如&#xff0c;当我们面临多个项目并行时&#xff0c;通过管理软件可以一目了然地查…