C++初阶学习————二叉树进阶(二叉搜索树)-创新互联

二叉树进阶
  • 二叉搜索树的概念
  • 二叉搜索树的操作
    • 基本框架
    • 二叉搜索树的插入
    • 二叉搜索树的查找
    • 二叉搜索树的删除
  • 整体代码
    • 循环写法
    • 递归写法
  • 二叉搜索树的应用
  • 二叉搜索树的性能分析

前面的文章介绍过二叉树的基础概念以及完全二叉树的应用等等,本篇将介绍二叉搜索树

创新互联建站坚持“要么做到,要么别承诺”的工作理念,服务领域包括:成都网站建设、成都网站设计、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的城中网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!二叉搜索树的概念

二叉搜索树是一颗排序树,或者是一颗空树如图:

在这里插入图片描述
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树,并且它可以去重

二叉搜索树的操作 基本框架
templatestruct BSTreeNode
{BSTreeNode* _left;
	BSTreeNode* _right;
	T _key;
	
	BSTreeNode(const T& key)
		:_left(nullptr)
		,_right(nullptr)
		,_key(key)
	{}
};
templateclass BSTree
{typedef BSTreeNodeNode;
public:
	BSTree()
		:_root(nullptr)
	{}
private:
	Node* _root;
};
二叉搜索树的插入

例如插入6和10
在这里插入图片描述

bool insert(const T& key)
{if (_root == nullptr)
	{_root = new Node(key);
		return true;
	}

	Node* cur = _root;
	Node* parent = cur;
	while (cur)
	{if (cur->_key >key)
		{	parent = cur;
			cur = cur->_left;
		}
		else if (cur->_key< key)
		{	parent = cur;
			cur = cur->_right;
		}
		else
			return false;
	}
	cur = new Node(key);
	if (parent->_key >key)
		parent->_left = cur;
	else
		parent->_right = cur;

	return true;
}

注意:会有树为空的情况需要判断

二叉搜索树的查找

查找的代码和插入非常类似,大于根去右边,小于根去左边,最多查找高度次
代码如下:

//Node* find(const T& key)
bool find(const T& key)
{if (_root == nullptr)
		return false;
	//return nullptr;

	Node* cur = _root;
	while (cur)
	{if (cur->_key >key)
			cur = cur->_left;
		else if (cur->_key< key)
			cur = cur->_right;
		else
			return true;
		//return cur;
	}
	return false;
	//return nullptr;
}
二叉搜索树的删除

这里的删除相对于插入和查找来说是比较难的,最重要的是不能改变二叉搜索树的结构,所以这里主要分为两种情况考虑

  1. 删除只有一个子节点或没有子节点
  2. 删除左右子节点都存在的

先找到要删除的节点,代码如下:

bool erase(const T& key)
{if (_root == nullptr)
		return false;

	Node* cur = _root;
	Node* parent = cur;
	while (cur)
	{if (cur->_key >key)
		{	parent = cur;
			cur = cur->_left;
		}
		else if (cur->_key< key)
		{	parent = cur;
			cur = cur->_right;
		}
		else		
		{	删除部分的代码........
		}
	}
	return false;	
}

1. 删除只有一个子节点或没有子节点

(1)删除叶子节点(删除9举例)
在这里插入图片描述

(2)删除只存在一个子节点的节点(删除8举例)
在这里插入图片描述

情况1的删除代码:

else
{if (cur->_left == nullptr)
	{if (cur == parent->_left)
			parent->_left = cur->_right;
		else
			parent->_right = cur->_right;
	}
	else if (cur->_right == nullptr)
	{if (cur == parent->_left)
			parent->_left = cur->_left;
		else
			parent->_right = cur->_left;
	}
	else
	{情况2部分的代码.......}
	delete cur;
}

除上述代码删除的示例外,另外三种情况
在这里插入图片描述
所以这里需要仔细处理链接,否则会破坏搜索树的结构

2.删除左右节点都存在的节点

(1)删除根节点
在这里插入图片描述
在这里插入图片描述

(2)删除其他存在左右子节点的节点
在这里插入图片描述
在这里插入图片描述
找到的右子树最小节点一定没有左节点,且它的父节点一定不为空;所以在交换或覆盖完数值后,剩下的步骤和情况一一样,并且这里不用单独判断删除的节点存在哪边的子节点(右子树的最小节点一定没有左节点,所以只可能存在右节点)
在这里插入图片描述
情况2的删除代码:

else
{Node* min_parent = cur;
	Node* min = cur->_right;

	while (min->_left)
	{min_parent = min;
		min = min->_left;
	}
	cur->_key = min->_key;

	if (min == min_parent->_left)
		min_parent->_left = min->_right;
	else
		min_parent->_right = min->_right;

	swap(min,cur);
}

在这里插入图片描述
以上就是删除主要步骤,但还有个问题就是要考虑只有一个节点,需要操作根节点。

if (_root->_key == key )
{if (_root->_left == nullptr)
		_root = _root->_right;
	else
		_root = _root->_left;
}

总体代码

bool erase(const T& key)
{if (_root == nullptr)
		return false;

	Node* cur = _root;
	Node* parent = cur;
	while (cur)
	{if (cur->_key >key)
		{	parent = cur;
			cur = cur->_left;
		}
		else if (cur->_key< key)
		{	parent = cur;
			cur = cur->_right;
		}
		else
		{	if (_root->_key == key )
			{		if (_root->_left == nullptr)
					_root = _root->_right;
				else
					_root = _root->_left;
			}
			else if (cur->_left == nullptr)
			{		if (cur == parent->_left)
					parent->_left = cur->_right;
				else
					parent->_right = cur->_right;
			}
			else if (cur->_right == nullptr)
			{		if (cur == parent->_left)
					parent->_left = cur->_left;
				else
					parent->_right = cur->_left;
			}
			else
			{		Node* min_parent = cur;
				Node* min = cur->_right;

				while (min->_left)
				{min_parent = min;
					min = min->_left;
				}
				cur->_key = min->_key;

				if (min == min_parent->_left)
					min_parent->_left = min->_right;
				else
					min_parent->_right = min->_right;

				swap(min,cur);
			}
			delete cur;
			return true;
		}
	}
	return false;
}
整体代码 循环写法
bool insert(const T& key)
	{if (_root == nullptr)
		{	_root = new Node(key);
			return true;
		}

		Node* cur = _root;
		Node* parent = cur;
		while (cur)
		{	if (cur->_key >key)
			{		parent = cur;
				cur = cur->_left;
			}
			else if (cur->_key< key)
			{		parent = cur;
				cur = cur->_right;
			}
			else
				return false;
		}
		cur = new Node(key);
		if (parent->_key >key)
			parent->_left = cur;
		else
			parent->_right = cur;

		return true;
	}


	bool find(const T& key)
	{if (_root == nullptr)
			return false;
		//return nullptr;

		Node* cur = _root;
		while (cur)
		{	if (cur->_key >key)
				cur = cur->_left;
			else if (cur->_key< key)
				cur = cur->_right;
			else
				return true;
			//return cur;
		}
		return false;
		//return nullptr;
	}


	bool erase(const T& key)
	{if (_root == nullptr)
			return false;

		Node* cur = _root;
		Node* parent = cur;
		while (cur)
		{	if (cur->_key >key)
			{		parent = cur;
				cur = cur->_left;
			}
			else if (cur->_key< key)
			{		parent = cur;
				cur = cur->_right;
			}
			else
			{		if (_root->_key == key )
				{if (_root->_left == nullptr)
						_root = _root->_right;
					else
						_root = _root->_left;
				}
				else if (cur->_left == nullptr)
				{if (cur == parent->_left)
						parent->_left = cur->_right;
					else
						parent->_right = cur->_right;
				}
				else if (cur->_right == nullptr)
				{if (cur == parent->_left)
						parent->_left = cur->_left;
					else
						parent->_right = cur->_left;
				}
				else
				{Node* min_parent = cur;
					Node* min = cur->_right;

					while (min->_left)
					{min_parent = min;
						min = min->_left;
					}
					cur->_key = min->_key;

					if (min == min_parent->_left)
						min_parent->_left = min->_right;
					else
						min_parent->_right = min->_right;

					swap(min,cur);
				}
				delete cur;
				return true;
			}
		}
		return false;
	}

	//打印搜索树
	void print_BSTree()
	{_print_BSTree(_root);
		cout<< endl;
	}
	void _print_BSTree(Node* root)
	{if (root == nullptr)
			return;

		_print_BSTree(root->_left);
		cout<< root->_key<< " ";
		_print_BSTree(root->_right);
	}
递归写法

1.插入

bool insertR(const T& key)
{return insertR(_root,key)
}
bool _insertR(Node*& root,const T& key)
{if(root == nullptrd)
	{root = new Node(key);
		return true;
	}
	if(root->_key >key)
		return insertR(root->_left,key);
	else if(root->_key< key)
		return insertR(root->_right,key);
	else
		return false;
}

注意,这里可以成功链接上,主要是运用了引用,递的是左右指针的引用
在这里插入图片描述

2.查找

Node* findR(const T& key)
{if()
}
bool _insertR(Node*& root, const T& key)
{if (root == nullptr)
	{root = new Node(key);
		return true;
	}
	if (root->_key >key)
		return _insertR(root->_left, key);
	else if (root->_key< key)
		return _insertR(root->_right, key);
	else
		return false;
}

3.删除

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

bool _EraseR(Node*& root, const T& key)
{if (root == nullptr)
		return false;

	if (root->_key >key)
		_EraseR(root->_left, key);
	else if (root->_key< key)
		_EraseR(root->_right, key);
	else
	{Node* era = root;
		if (root->_left == nullptr)
			root = root->_right;
		else if (root->_right == nullptr)
			root = root->_left;
		else
		{	Node* min = root->_right;
			Node* min_parent = root;
			while (min->_left)
			{		min_parent = min;
				min = min->_left;
			}
			swap(min->_key, root->_key);
			return _EraseR(root->_right, key);
		}
		delete era;
		return true;
	}
}

这里讲一下删除的思路:
情况一时,操作和递归的插入基本一样,通过使用引用就可直接令要删除节点的父节点与子节点直接相连,也不用再去判断删除节点是父节点的左边还是右边

情况二时,先找到右子树最小节点,再与要删除的节点交换数值,最后递归传递的节点是,要删除节点的左子树(情况二再交换完数值后就可以按照情况一去处理了)

在这里插入图片描述

注意:要提前记录一下找到的要删除的节点,中间过程会改变root的值,所以最后删除的是提前记录好的节点

总体代码

bool insertR(const T& key)
	{return _insertR(_root,key);
	}
	Node* findR(const T& key)
	{return _findR(_root,key);
	}

	bool EraseR(const T& key)
	{return _EraseR(_root, key);
	}



private:
	bool _insertR(Node*& root, const T& key)
	{if (root == nullptr)
		{	root = new Node(key);
			return true;
		}
		if (root->_key >key)
			return _insertR(root->_left, key);
		else if (root->_key< key)
			return _insertR(root->_right, key);
		else
			return false;
	}

	Node* _findR(Node* root, const T& key)
	{if (root == nullptr)
			return nullptr;

		if (root->_key >key)
			return _findR(root->_left, key);
		else if (root->_key< key)
			return _findR(root->_right, key);
		else
			return root;

	}

	bool _EraseR(Node*& root, const T& key)
	{if (root == nullptr)
			return false;

		if (root->_key >key)
			_EraseR(root->_left, key);
		else if (root->_key< key)
			_EraseR(root->_right, key);
		else
		{	Node* era = root;
			if (root->_left == nullptr)
				root = root->_right;
			else if (root->_right == nullptr)
				root = root->_left;
			else
			{		Node* min = root->_right;
				Node* min_parent = root;
				while (min->_left)
				{min_parent = min;
					min = min->_left;
				}
				swap(min->_key, root->_key);
				return _EraseR(root->_right, key);
			}
			delete era;
			return true;
		}
	}
二叉搜索树的应用
  1. K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。
    比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:
    • 以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
    • 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。
  2. KV模型:每一个关键码key,都有与之对应的值Value,即的键值对。该种方式在现实生活中非常常见:
    • 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文就构成一种键值对;
    • 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是就构成一种键值对

c++提供了这种键值对儿的结构,叫做pair

pairp1("one",1);
p1.first就是key
p1.second就是value
make_pair("one",p1); 可以构造一个pair

在这里插入图片描述

二叉搜索树的性能分析

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

但是会有极端情况:当插入的值顺着一个方向一直插入(即插入的值一直是最小或大或者接近这种情况),那么二叉树就变成了单树,这时的查找效率就是极低了,即结点越深,则比较次数越多。

  • 最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为: l o g 2 N log_2 N log2​N
  • 最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为: N 2 \frac{N}{2} 2N​
    在这里插入图片描述
    当变为单树就失去意义了(查找元素相当于在顺序表中搜索元素,效率低下)。

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


网页题目:C++初阶学习————二叉树进阶(二叉搜索树)-创新互联
分享URL:http://ybzwz.com/article/ddjgeg.html