二叉搜索树递归&&非递归的基本实现

学习二叉树之后,发现二叉搜索树其实是很实用的,为什么呢?其实取决于它所具有的性质,下来简单介绍一下它的性质。

二叉搜索树的性质:

1. 每个节点都有一个作为搜索依据的关键码(key),所有节点的关键码互不相同。 

2. 左子树上所有节点的关键码(key)都小于根节点的关键码(key)。

3. 右子树上所有节点的关键码(key)都大于根节点的关键码(key)。

4. 左右子树都是二叉搜索树。



下来实现一些二叉树的基本功能

1.首先我们先看一下查找节点:

@在查找节点时,其核心思想就是用查找节点的关键码与当前节点的关键码进行比较。

非递归实现:

template<class T>
bool SearchBinaryTree<T>::Find(const T& key)
{
	Node* cur=_root;
	while(cur)
	{
		if (cur->_key < key)
		{
			cur=cur->_right;
		}
		else if (cur->_key > key)
		{
			cur=cur->_left;
		}
		else
		{
			return true;
		}
	}
	return false;
}


递归实现:

template<class T>
bool SearchBinaryTree<T>::FindR(const T& key)
{
	return _FindR(_root,key);
}
template<class T>
bool SearchBinaryTree<T>::_FindR(Node* &root,const T& key)
{
	if (root==NULL)
	{
		return false;
	}
	if (root->_key < key)
		return _FindR(root->_right,key);

	else if (root->_key > key)
		return _FindR(root->_left,key);
	else
		return true;
}



2.向搜索二叉树中插入节点

插入时有以下几种情况:

@空树的话直接开辟一个节点插入树中即可;

@若不为空树,需要明确一点,为了使插入节点后依然保持搜索二叉树的性质,所以首先应该找到要插入的节点位置;找到位置后把新节点作为叶子节点插入。

@还需注意的是搜索二叉树不允许有重复数据,所以插入时应注意如果该关键码已存在就可以直接返回了。


非递归:

template<class T>
bool SearchBinaryTree<T>::Insert(const T& key)
{
	//1.二叉树为空
	//2.要插入的值已经存在
	//3.不存在

	if(NULL==_root)
	{
		_root=new Node(key);
		return true;
	}
	Node* cur=_root;
	Node* parent=NULL;
	while(cur)
	{
		parent=cur;
		if (cur->_key < key)
		{
			cur=cur->_right;
		}
		else if(cur->_key > key)
		{
			cur=cur->_left;
		}
		else         //cur->_key==key(不重复插入)
			return false;
	}

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


递归:

template<class T>
bool SearchBinaryTree<T>::InsertR(const T& key)      //递归插入
{
	return _InsertR(_root,key);

}
template<class T>
bool SearchBinaryTree<T>::_InsertR(Node* &root,const T& key)      
{
	if (root==NULL)
	{
		root=new Node(key);
		return true;
	}
	if (root->_key < key)
	{
		return _InsertR(root->_right,key);
	}
	else if (root->_key >key)
	{
		return _InsertR(root->_left,key);
	}
	else
		return false;   //root->_key==key && root!=NULL
}


3.删除

删除结点稍微有些麻烦了,因为要保持搜索二叉树的性质。

首先需要在树中找到要删除的结点,如果删除的是叶子结点的话就比较好删除,只要让父节点的left或者right指向空就好了(至于是父节点的right,还是父节点的left,递归和非递归的判别方法不一样,具体看代码),如果不是叶子结点呢,又可以分为三种情况: 
第一种:当前结点的左为NULL,那么就让父节点的left或者right指向当前结点的右子树; 
第二种:当前结点的右为NULL,那么就让父节点的left或者right指向当前结点的左子树; 
其实可以看出来,要删除的是叶子结点的话也可归为左子树为NULL或者右子树为NULL的情况,因为叶子结点的左右子树都为NULL。

如下图所示(右为空类似):


这里还得注意一个问题:
在删除根节点时,根节点只有一个子树,即按照左子树为NULL或者右子树为NULL的情况处理,此时根节点的parent是NULL,所以需要单独处理!

第三种:左右子树都不为NULL的情况 
这种情况就需要想一下如何在不改变树性质的情况下,达到删除的目的。我们可以想想在删除一个无头指针的指定结点时是怎么做的?因为是没有头指针的,所以我们无法从头遍历链表,因此就不知道当前删除结点的前一个结点,那么,解决的办法就是把当前结点的值和另一个结点的值进行交换,然后删除另一个结点就好了; 我们在搜索二叉树这里的原理也是类似的,我们可以把当前需要删除的非叶子结点和一个比较容易删除的结点的key值进行交换,然后删除那个比较容易删除的结点; 
那么,怎么找到这个比较容易删除的结点,可以交换key值并且不改变树的性质? 
有两种方法,我们可以和当前结点左子树的最右结点的key值交换;或者和当前结点的右子树的最左结点的key值交换;

因为交换之后还要保持当前结点的key值比它的左子树的key值大,比右子树的key值小,所以只有以上两种情况的结点可以满足要求。 

非递归实现:

<span style="font-size:14px;">template<class T>
bool SearchBinaryTree<T>::Remove(const T& key)  
{  
	Node* cur=_root;  
	Node* parent = NULL;  
	while (cur)  
	{  
		if (cur->_key < key)  
		{  
			parent = cur;  
			cur = cur->_right;  
		}  
		else if (cur->_key>key)  
		{  
			parent = cur;  
			cur = cur->_left;  
		}  
		else  
		{  //找到开始删除
			if (cur->_left == NULL)     //如果cur的左孩子为空,或左右都为空(叶子节点的情况包括在内)  
			{  
				if (parent == NULL)  
					_root = _root->_right;           
				else  
				{  
					if (parent->_left==cur)  
						parent->_left = cur->_right;  
					else  
						parent->_right = cur->_right;  
				}  
				delete cur;  
			}  
			else if (cur->_right == NULL)    //如果cur的右孩子为空,或左右都为空(叶子节点的情况包括在内)  
			{  
				if (parent == NULL)  
					_root = _root->_left; 

				else  
				{  
					if (parent->_left == cur)  
						parent->_left = cur->_left;  
					else  
						parent->_right = cur->_left;  
				}  
				delete cur;  
			} 
			else            
			{  
				//左右节点均不为空的情况,通常采用找右子树的最左节点
				//或找左子树的最右节点进行替换的方法
				parent=cur;
				Node* MinRight = cur->_right;  
				while (MinRight->_left)  
				{   
					parent=MinRight;
					MinRight=MinRight->_left;  
				}  
				//swap(cur->_key,MinRight->_key);      //交换两个结点的值
				cur->_key=MinRight->_key;
				if (parent->_left == MinRight)  
				{  
					parent->_left=MinRight->_right; 
				}  
				else  
					parent->_right=MinRight->_right;

				delete MinRight;                   
			}
			return true;
		}
	}  
	return false;  
}  </span><span style="font-size: 15px;">
</span>



递归:

template<class T>
bool SearchBinaryTree<T>::RemoveR(const T& key)
{
	return _RemoveR(_root,key);
}

template<class T>
bool SearchBinaryTree<T>::_RemoveR(Node* &root,const T& key)
{
	assert(root);
	if (root==NULL)
	{
		return false;
	}
	if (root->_key < key)
		return _RemoveR(root->_right,key);
	else if(root->_key > key)
		return _RemoveR(root->_left,key);
	else
	{
		Node*del=root;
		if (root->_left==NULL)
			root=root->_right;
		else if(root->_right==NULL)
			root=root->_left;
		else    //左右孩子都不为空
		{
			Node* parent=root;
			Node* MinRight=root->_right;
			while (MinRight->_left)
			{
				parent=MinRight;
				MinRight=MinRight->_left;
			}

			root->_key=MinRight->_key;
			del=MinRight;

			if (parent->_left==MinRight)
				parent->_left=MinRight->_right;
			else
				parent->_right=MinRight->_right;
		}
		delete del;
		del=NULL;
		return true;
	}
}


完整代码可在github上看到:

点击打开链接







  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值