二叉树进阶

1 二叉搜索树概念

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

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树
    eg:
    int a [] = {5,3,4,1,7,8,2,6,0,9}
    在这里插入图片描述

2 二叉搜索树操作

1. 二叉搜索树的查找

  • 普通数组链表:暴力查找 — O(N)
  • 排序数组:二分查找 — O(logN)
  • 二叉搜索树结构查找一个值,最多查找高度次
  • 二叉树中插入一个数,有唯一的位置。
    在这里插入图片描述

2. 二叉搜索树的插入
插入的具体过程如下:

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

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

  • a. 要删除的结点无孩子结点
  • b. 要删除的结点只有左孩子结点
  • c. 要删除的结点只有右孩子结点
  • d. 要删除的结点有左、右孩子结点
    看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程如下:
    情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点
    情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点
    情况d:在它的右子树中寻找最小值,用它的值填补到被删除节点中,再来处理该结点的删除问题。

3 二叉搜索树的实现

递归版实现:

namespace KEY
{
	template<class K>
	struct BSTNode
	{
		//构造
		BSTNode(const K& key)
			: _left(nullptr)
			, _right(nullptr)
			, _key(key)
		{}

		BSTNode* _left;
		BSTNode* _right;
		K _key;
	};

	template<class K>
	class BSTreeR
	{
		typedef BSTNode<K> Node;
	public:
		//构造
		BSTreeR()
			: _root(nullptr)
		{}
		//拷贝构造
		BSTreeR(BSTreeR& t)
		{
			_root = Copy(t._root);
		}
		//拷贝函数
		Node* Copy(Node* root)
		{
			if (root == nullptr)
			{
				return nullptr;
			}
			//先new出一个节点
			Node* newRoot = new Node(root->_key);
			newRoot->_left = Copy(root->_left);
			newRoot->_right = Copy(root->_right);
			return newRoot;
		}
		//赋值操作符重载
		BSTreeR<K>& operator=(BSTreeR<K> t)
		{
			std::swap(this->_root, t->_root);
			return *this;
		}
		//析构函数
		~BSTreeR()
		{
			Destory(_root);
		}
		void Destory(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			Destory(root->_left);
			Destory(root->_right);
			delete root;
		}
		//插入函数
		bool Insert(const K& key)
		{
			return _InsertR(_root, key);
		}
		//中序遍历函数
		void InOrderR()
		{
			_InOrderR(_root);
			cout << endl;
		}
		//查找函数
		Node* Find(const K& key)
		{
			return _FindR(_root, key);
		}
		//删除函数
		bool Erase(const K& key)
		{
			return _EraseR(_root, key);
		}
	private:
		//递归版插入函数
		bool _InsertR(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				//插入
				root = new Node(key);
			}
			else
			{
				//查找
				if (key == root->_key)
				{
					return false;
				}
				else if (key > root->_key)
				{
					return _InsertR(root->_right, key);//到右树去找
				}
				else
				{
					return _InsertR(root->_left, key);//到左树去找
				}
			}
			return true;
		}
		//递归版中序遍历函数
		void _InOrderR(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			_InOrderR(root->_left);
			cout << root->_key << " ";
			_InOrderR(root->_right);
		}
		//递归版查找函数
		Node* _FindR(Node* root, const K& key)
		{
			if (root == nullptr)
			{
				return nullptr;
			}
			else
			{
				if (root->_key == key)
				{
					return root;
				}
				else if (key < root->_key)
				{
					return _FindR(root->_left, key);
				}
				else
				{
					return _FindR(root->_right, key);
				}
			}
		}
		//递归版删除函数
		bool _EraseR(Node*& root, const K& key)
		{
			//找不到key,或空树情况
			if (root == nullptr)
			{
				return false;
			}
			else
			{
				//查找
				if (key > root->_key)
				{
					_EraseR(root->_right, key);
				}
				else if (key < root->_key)
				{
					_EraseR(root->_left, key);
				}
				else
				{
					//提前记录要删除的位置
					Node* tmp = root;
					//找到了,开始删除
					if (root->_left == nullptr)
					{
						//该节点左为空
						root = root->_right;
					}
					else if (root->_right == nullptr)
					{
						//该节点右为空
						root = root->_left;
					}
					else
					{
						//左右都不为空
						Node* minRight = root->_right;
						//在右树中找最小值,即右树中的最左值
						while (minRight->_left)
						{
							minRight = minRight->_left;
						}
						//将要删除的值用右树中的最左值替代
						root->_key = minRight->_key;
						//删除右树中的最左值
						return _EraseR(root->_right, minRight->_key);
					}
					//释放删除节点的空间
					delete tmp;
				}
				return true;
			}
		}
		Node* _root = nullptr;
	};
}
void test_BSTree_R()
{
	KEY::BSTreeR<int> t1;
	int a[] = { 5,3,4,1,7,8,2,6,0,9 };
	cout << "t1:";
	for (auto e : a)
	{
		t1.Insert(e);
	}
	t1.InOrderR();

	KEY::BSTreeR<int> t2 = t1;
	cout << "t2:";
	t2.InOrderR();

	KEY::BSTreeR<int> t(t1);
	cout << "t :";
	t.InOrderR();
	KEY::BSTNode<int>* F;
	F = t.Find(3);
	F = t.Find(5);
	F = t.Find(7);
	F = t.Find(1);
	for (auto e : a)
	{
		t.Erase(e);
		t.InOrderR();
	}
	cout << "程序结束" << endl;
}

int main()
{
	//KEY模型测试
	test_BSTree_R();
	return 0;
}

4 二叉搜索树的应用

1. K模型

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

2. KV模型

KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。
比如:

  • 英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对;
  • 统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对。

eg:
实现一个简单的英汉词典dict可以通过英文找到与其对应的中文,具体实现方式如下:

  • <单词,中文含义>为键值对构造二叉搜索树,注意:二叉搜索树需要比较,键值对比较时只比较Key
  • 查询英文单词时,只需给出英文单词,就可快速找到与其对应的key
namespace KEY_VALUE
{
	template<class K, class V>
	struct BSTNode
	{
		//构造
		BSTNode(const K& key, const V& value)
			: _left(nullptr)
			, _right(nullptr)
			, _key(key)
			, _value(value)
		{}

		BSTNode* _left;
		BSTNode* _right;
		K _key;
		V _value;
	};

	template<class K,class V>
	class BSTreeR
	{
		typedef BSTNode<K, V> Node;
	public:
		//构造
		BSTreeR()
			: _root(nullptr)
		{}
		//拷贝构造
		BSTreeR(BSTreeR& t)
		{
			_root = Copy(t._root);
		}
		//拷贝函数
		Node* Copy(Node* root)
		{
			if (root == nullptr)
			{
				return nullptr;
			}
			//先new出一个节点
			Node* newRoot = new Node(root->_key, root->_value);
			newRoot->_left = Copy(root->_left);
			newRoot->_right = Copy(root->_right);
			return newRoot;
		}
		//赋值操作符重载
		BSTreeR<K, V>& operator=(BSTreeR<K, V> t)
		{
			std::swap(this->_root, t->_root);
			return *this;
		}
		//析构函数
		~BSTreeR()
		{
			Destory(_root);
		}
		void Destory(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			Destory(root->_left);
			Destory(root->_right);
			delete root;
		}
		//插入函数
		bool Insert(const K& key, const V& value)
		{
			return _InsertR(_root, key, value);
		}
		//中序遍历函数
		void InOrderR()
		{
			_InOrderR(_root);
			cout << endl;
		}
		//查找函数
		Node* Find(const K& key)
		{
			return _FindR(_root, key);
		}
		//删除函数
		bool Erase(const K& key)
		{
			return _EraseR(_root, key);
		}
	private:
		//递归版插入函数
		bool _InsertR(Node*& root, const K& key, const V& value)
		{
			if (root == nullptr)
			{
				//插入
				root = new Node(key, value);
			}
			else
			{
				//查找
				if (key == root->_key)
				{
					return false;
				}
				else if (key > root->_key)
				{
					return _InsertR(root->_right, key ,value);//到右树去找
				}
				else
				{
					return _InsertR(root->_left, key , value);//到左树去找
				}
			}
			return true;
		}
		//递归版中序遍历函数
		void _InOrderR(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			_InOrderR(root->_left);
			cout << root->_key << root->_value << " ";
			_InOrderR(root->_right);
		}
		//递归版查找函数
		Node* _FindR(Node* root, const K& key)
		{
			if (root == nullptr)
			{
				return nullptr;
			}
			else
			{
				if (root->_key == key)
				{
					return root;
				}
				else if (key < root->_key)
				{
					return _FindR(root->_left, key);
				}
				else
				{
					return _FindR(root->_right, key);
				}
			}
		}
		//递归版删除函数
		bool _EraseR(Node*& root, const K& key)
		{
			//找不到key,或空树情况
			if (root == nullptr)
			{
				return false;
			}
			else
			{
				//查找
				if (key > root->_key)
				{
					_EraseR(root->_right, key);
				}
				else if (key < root->_key)
				{
					_EraseR(root->_left, key);
				}
				else
				{
					//提前记录要删除的位置
					Node* tmp = root;
					//找到了,开始删除
					if (root->_left == nullptr)
					{
						//该节点左为空
						root = root->_right;
					}
					else if (root->_right == nullptr)
					{
						//该节点右为空
						root = root->_left;
					}
					else
					{
						//左右都不为空
						Node* minRight = root->_right;
						//在右树中找最小值,即右树中的最左值
						while (minRight->_left)
						{
							minRight = minRight->_left;
						}
						//将要删除的值用右树中的最左值替代
						root->_key = minRight->_key;
						//删除右树中的最左值
						return _EraseR(root->_right, minRight->_key);
					}
					//释放删除节点的空间
					delete tmp;
				}
				return true;
			}
		}
		Node* _root = nullptr;
	};
}
void test_K_Value_R()
{
	KEY_VALUE::BSTreeR<string,string> t1;
	t1.Insert("world", "单词");
	t1.Insert("sort", "删除");
	t1.Insert("left", "单词");
	t1.Insert("right", "单词");
	cout << "t1:";
	t1.InOrderR();

	KEY_VALUE::BSTreeR<string, string> t2 = t1;
	cout << "t2:";
	t2.InOrderR();

	KEY_VALUE::BSTreeR<string, string> t3(t1);
	cout << "t3:";
	t3.InOrderR();

	cout << "程序结束" << endl;
}

void COUT_CHINESE()
{
	KEY_VALUE::BSTreeR<string, string> t1;
	t1.Insert("world", "单词");
	t1.Insert("sort", "删除");
	t1.Insert("left", "单词");
	t1.Insert("right", "单词");
	string str;
	cout << "输入Q退出" << endl;
	while (cin >> str)
	{
		KEY_VALUE::BSTNode<string, string>* ptr = t1.Find(str);
		if (str == "Q")
		{
			return;
		}
		else
		{
			if (ptr == nullptr)
			{
				cout << "输入错误" << endl;
			}
			else
			{
				cout << ptr->_key << "->" << ptr->_value << endl;
			}
		}
	}
}

int main()
{
	//K_VALUE版
	test_K_Value_R();
	//查找单词
	COUT_CHINESE();
	return 0;
}

5 二叉搜索树的性能分析

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
二叉搜索树结构查找一个值,最多查找高度次。但关键码插入的次序不同,可能得到不同结构的二叉搜索树
在这里插入图片描述
最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:logN
最差情况下,二叉搜索树退化为单支树,其平均比较次数为:N/2
要按最差情况看,所以二叉搜索树时间复杂度为O(N)。

问题:

如果退化成单支树,二叉搜索树的性能就失去了。那能否进行改进,不论按照什么次序插入关键码,都可以是二叉搜索树的性能最佳?
后面一篇博客讲解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值