搜索二叉树的算法解析与实例演示

在这里插入图片描述

一.搜索二叉树的特性与实现

1.特点

二叉搜索树是特殊的二叉树,它有着更严格的数据结构特点:
(1)非空左子树的所有键值小于其根结点的键值。
(2)非空右子树的所有键值大于其根结点的键值。
(3)左、右子树都是二叉搜索树
如下图所示就是一株典型的搜索二叉树:
在这里插入图片描述
这种结构中序遍历的结果是升序的,以上特性可以帮助我们解决很多问题。例如查找

2.实现

搜索二叉树的查找功能逻辑较简单,根据要查找的值依次与当前节点键值比较,如果小于就在左树继续寻找反之在右树查找

	bool find(const T& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key > key)
			{
				cur = cur->left;
			}
			else if (cur->_key < key)
			{
				cur = cur->right;
			}
			else
			{
				return true;
			}
		}

		return false;
	}

插入功能首先要查找到要插入的值应该放的地方,接着判断自己是父节点的左树还有右树然后进行插入,当查找到与要插入的值相同的键值时,插入失败,因为搜索二叉树不允许有相同的值存在,需要注意的是当此树为空树时,直接插入值即可:

bool Insert(const T& key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);
			return true;
		}
		Node* cur = _root;
		Node* parents = nullptr;
		while (cur)
		{
			if (cur->_key < key)
			{
				parents = cur;
				cur = cur->right;
			}
			else if (cur->_key > key)
			{
				parents = cur;
				cur = cur->left;
			}
			else
			{
				return false;
			}
		}

		cur = new Node(key);
		if (parents->_key > key )
		{
			parents->left = cur;
		}
		else
		{
			parents->right = cur;
		}
		return true;
	}

需要重点理解是接下来的删除功能:要删除某个键值 首先需要找到这个结点,大体逻辑与find功能相似,不同的是在找到时如何删除这个结点。这时候分为三种情况:结点左子树为空/结点右子树为空/左右子树都不为空。左或右子树为空时逻辑较简单使用托孤法即可首先判断是否为根节点如果是则直接将根节点赋值为根节点的右或左结点接着判断自己是父节点的左/右结点,接着让父节点的左/右直接指向我的左或右结点。 当左右子树都不为空时需要使用到替换法,将要删除的结点与左子树的最大结点的键值或者右子树的最小节点的键值替换(因为要保证搜索二叉树的特性),之后删除即可。

bool Erase(const T& key)
	{
		Node* cur = _root;
		Node* parents = nullptr;

		if (cur == nullptr)
			return false;
		while (cur)
		{
			if (cur->_key > key)
			{
				parents = cur;
				cur = cur->left;
			}
			else if (cur->_key < key)
			{
				parents = cur;
				cur = cur->right;
			}
			else
			{
				if (cur->left == nullptr)
				{	if (cur == _root)
					{
						_root = cur->right;
					}
					else
					{
						if (parents->left == cur)
						{
							parents->left = cur->right;
						}
						else
						{
							parents->right = cur->right;
						}
					}
				}
				else if (cur->right == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->left;
					}
					else
					{
						if (parents->left == cur)
						{
							parents->left = cur->left;
						}
						else
						{
							parents->right = cur->left;
						}
					}
				}
				else//左子树和右子树都不为空
				{
					Node* parents = cur;
					Node* leftmax = cur->left;
					while (leftmax->right)
					{
						parents = leftmax;
						leftmax = leftmax->right;
					}
					swap(cur->_key, leftmax->_key);

					if (parents->left == leftmax)
					{
						parents->left = leftmax->left;
					}
					else
					{
						parents->right = leftmax->left;
					}
					cur = leftmax;

				}
				delete cur;
				return true;
			}
		}
		return false;
	}

以上讲解的是非递归版的实现,递归版的查找 插入 删除代码更为简洁,但是更难理解。
因为在类外调用成员函数无法向函数传参成员变量,所以在类中进行一些封装

public:

	bool findR(const T& key)
	{
		return _findR(_root, key);
	}
	
private:

	bool _findR(Node* root, const T& key)
	{
		if (root == nullptr)
		{
			return false;
		}
		if (root->_key > key)
		{
			return _findR(root->left, key);
		}
		else if (root->_key < key)
		{
			return _findR(root->right.key);
		}
		else
		{
			return true;
		}
	}

查找与删除功能同样使用了封装:

public:

	bool InsertR(const T& key)
	{
		return _InsertR(_root, key);
	}

	bool EraseR(const T& key)
	{
		return _EraseR(_root, key);
	}
	
private:
	
	bool _EraseR(Node*& root, const T& key)
	{
		if (root == nullptr)
		{
			return false;
		}
		if (root->_key > key)
		{
			return _EraseR(root->left, key);
		}
		else if (root->_key < key)
		{
			return _EraseR(root->right,key);
		}
		else
		{
			Node* del = root;
			if (root->left == nullptr)
			{
				root = root->right;
			}
			else if (root->right == nullptr)
			{
				root = root->left;
			}
			else
			{
				Node* leftMax = root->left;
				while (leftMax->right)
				{
					leftMax = leftMax->right;
				}

				swap(root->_key, leftMax->_key);

				return _EraseR(root->left, key);
			}
			delete del;
			return true;
		}
	}

	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;
		}
	}

二.搜索二叉树的性能

查找的性能在搜索二叉树为完全二叉树或者满二叉树或者接近时,时间复杂度是logN,
在这里插入图片描述
在极端情况下,时间复杂度平均为N/2.

  • 14
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
中科大算法导论实验,包括代码报告和可执行文件,vc++6.0下编程,c语言代码。 实验部分 一、要求 1.算法设计与分析1班,10月21日(周日)开始上课,晚上19:00-22:00。连续八周每周日在明德楼103实验室上实验课。 2.实验要求独立完成,发现抄袭则实验为0分(包括网上的代码),没有分组。 3.要求提交实验源码,可执行程序以及实验报告。实验报告包括程序的输入,输出,结果,演示界面,算法语言描述,原理等。要求把所有实验打包成一个rar文件后提交到教学系统,并且命名文件格式为学号+姓名(eg. 学号_NAME),不符合命名格式的一律不批改。 4.程序语言不做特别要求,C、C++、JAVA均可 5.实验提交截止时间: 2012/12/16 23:59:00 之前 二、题目 1.(必做题) 常见排序算法的实现与性能比较 问题描述:实现合并排序,插入排序,希尔排序,快速排序,冒泡排序,桶排序算法 实验要求: A. 在随机产生的空间大小分别为 N = 10, 1000,10000,100000 的排序样本(取值为[0,1])上测试以上算法。 B.结果输出: 1) N=10时,排序结果。 2) N=1000,10000,100000时,对同一个样本实例,不同排序完 成所需的时间。 3) N=1000,10000,100000时,每个排序用不同的样本多试验几 次(最低5次)得出平均时间,比较不同排序算法所用的平均时间。 文档要点:总结对各种排序的性能分析。 2. (必做题) 红黑树、二叉搜索树的实现和性能比较 问题描述: 实现红黑树、二叉搜索树相关算法:插入(红黑树涉及树的调整:左旋、右旋等),删除,搜索(指定Key值节点)。 另外,红黑树实现计算树黑高的算法。 实验要求: 1).插入测试,输入 8,11,17,15,6,1,22,25,27,建立红黑树,按照 红黑树信息输出方式 输出整棵红黑树以及黑高。 2).删除测试,删除1)中红黑树中Key=15的节点,按照 红黑树信息输出方式 输出调整后的整棵红黑树以及黑高。 3).随机产生300,000个不同自然数Key值(1-300,000,每个数出现一次,出现顺序随机),建立红黑树,查找Key=15000的节点,输出查找花费时间。 用上面的数据,建立二叉搜索树,查找Key=15000的节点,输出查找花费时间。 4). 重复3-5次3)中操作,求各自平均时间。 5). 在1)-4)的红黑树算法基础上修改完成P307 14.1-4算法 OS_Key_Rank(T,k). 输入 1,2,3,4,5,6,7,8 建树, k=6, 输出OS_Key_Rank的返回值。 文档要点:总结红黑树和二叉搜索树在查找上的性能分析,描述此类算法的应用。 附: 红黑树信息输出方式(右图) 3. (选做题) 最长递增子序列 问题描述: 随机生成小于等于n的自然数的一个序列,输出其最长递增子序列(任意一个即可)。 n 分别取 1000,3000,10000。 例: n=5 随机序列为 5 1 4 2 3,正确输出为1 2 3,即长度为3的递增子序列。 提示:参考LCS,思考能否达到时间复杂度(O(nlogn)) 文档要点:描述动态规划思想,总结时间和空间复杂度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aomnitrix

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值