数据结构之二叉搜索树(BinarySearchTree)

一。定义:二叉搜索树(Binary Search Tree),也称有序二叉树(ordered binary tree),排序二叉树(sorted binary tree),是指一棵空树或者具有下列性质的二叉树:

1. 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;

2. 若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;

3. 任意节点的左、右也分别为二叉查找树。

4. 没有键值相等的节点(no duplicate nodes)。

根据二叉搜索树的特点知:对二叉搜索树进行中序遍历得到的结点序列必然是一个有序序列。

一棵二叉搜索树:

    根据树的结构,我们可以封装一个结点的结构BSNode:

template<class K,class V>
struct BSNode
{
	BSNode<K, V>* _left;
	BSNode<K, V>* _right;
	K _key;
	V _value;
	BSNode(const K& key, const V& value)
		:_left(NULL)
		, _right(NULL)
		,_key(key)
		, _value(value)
	{}
};
    在这里树的结构封装了key/value形式的键值对。

二叉搜索树的结构我们更加关注的是它的增删查改功能。而且在这过程中我们要保证整棵树中没有重复的key值(结点的值)

所以封装一棵二叉搜索树BSTree:

template<class K,class V>
class BSTree
{
public:
	typedef BSNode<K, V> Node;<pre name="code" class="cpp">//增删改查功能

protected:
	Node* _root;
};

 

二。实现

    

    (一)插入新结点

<span style="font-size:14px;">bool Insert(const K& key, const V& value)//<span style="color:#ff0000;">非递归形式</span>
	{
		if (_root == NULL)
		{
			_root = new Node(key, value);
			return true;
		}
		Node* parent = NULL;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
				return false;
		}
		if (parent->_left == NULL && parent->_key > key)
		{
			parent->_left = new Node(key, value);
		}
		else if (parent->_right == NULL && parent->_key < key)
		{
			parent->_right = new Node(key, value);
		}
		return true;
	}</span>
<span style="font-size:14px;">bool Insert_R(const K& key, const V& value)//<span style="color:#ff0000;">插入的递归实现</span>
	{
		return _Insert_R(_root, key, value);
	}</span><pre name="code" class="cpp">bool _Insert_R(Node*& root,const K& key, const V& value)
	{
		if (root == NULL)
		{
			root = new Node(key, value);
			return true;
		}
		if (root->_key > key)
			_Insert_R(root->_left, key, value);
		else if (root->_key < key)
			_Insert_R(root->_right, key, value);
		else
			return false;
		return false;
	}

 
           
插入节点的思路就是递归的找要插入的位置,直到root为NULL,那么当前位置就是要插入的位置。 

(二)查找结点,返回该结点

Node* Find(const K& key) //<span style="color:#ff0000;">非递归实现</span>
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				cur = cur->_right;
			}
			else
				return cur;
		}
		return NULL;
	}
Node* Find_R(const K& key)//<span style="color:#ff0000;">递归实现</span>
	{
		return _Find_R(_root, key);
	}<pre name="code" class="cpp">Node* _Find_R(Node* root,const K& key)
	{
		if (root == NULL)
			return NULL;
		if (root->_key > key)
			_Find_R(root->_left, key);
		else if (root->_key < key)
			_Find_R(root->_right, key);
		else
			return root;
	}

       查找的实现就是分三种情况,当前结点key大于key,小于key,等于key。大于key递归的在当前结点的右子树查找,小于在左子树找,等于就返回结点。 

(三)删除结点,使剩下结点仍是一棵二叉搜索树

bool Remove(const K& key)//<span style="color:#ff0000;">非递归实现</span>
	{
		return _Remove(_root, key);
	}
bool _Remove(Node* root, const K& key)
	{
		if (root == NULL)
			return false;
		Node* parent = NULL;
		Node* cur = root;
		Node* del = NULL;
		while (cur)
		{
			if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				del = cur;
				//待删除结点左为空
				if (cur->_left == NULL)
				{
					if (parent && parent->_left == cur)
						parent->_left = cur->_right;
					else if (parent && parent->_right == cur)
						parent->_right = cur->_right;
					else
						_root = cur->_right;
				}
				else if (cur->_right == NULL)//待删除结点右为空
				{
					if (parent && parent->_left == cur)
						parent->_left = cur->_left;
					else if (parent &&parent->_right == cur)
						parent->_right = cur->_left;
					else
						_root = cur->_left;
				}
				else if (cur->_left == NULL && cur->_right == NULL)//待删除结点左右都为空
				{
					if (parent && parent->_left == cur)
						parent->_left = NULL;
					else if (parent && parent->_right == cur)
						parent->_right = NULL;
					else
						_root = NULL;
				}
				else if (cur->_left && cur->_right)//待删除结点左右都不为空
				{
					//找出右子树的最左结点
					Node* firstleft = cur->_right;
					parent = cur;
					while (firstleft->_left)
					{
						parent = firstleft;
						firstleft = firstleft->_left;
					}
					del = firstleft;
					swap(cur->_key, firstleft->_key);
					swap(cur->_value, firstleft->_value);
					//判断最左结点是它父节点的左结点还是右结点
					if (parent && parent->_left == firstleft)
					{
						parent->_left = firstleft->_right;
					}
					else if (parent && parent->_right == firstleft)
					{
						parent->_right = firstleft->_right;
					}
					else //parent==NULL。待删除结点的右边只有一个结点,则最左结点就是它
					{
						root->_right = NULL;
					}
				}
				delete del;
				return true;
			}
		}
		return false;
	}

	bool Remove_R(const K& key)//<span style="color:#ff0000;">递归实现</span>
	{
		return _Remove_R(_root, key);
	}<pre name="code" class="cpp">bool _Remove_R(Node*& root, const K& key)
	{
		if (root == NULL)
			return false;
		if (root->_key > key)
		{
			_Remove_R(root->_left, key); 
		}
		else if (root->_key < key)
		{
			_Remove_R(root->_right, key);
		}
		else
		{
			Node* del = root;
			if (root->_left == NULL&&root->_right == NULL)
			{
				root = NULL;
			}
			else if (root->_left == NULL)
			{
				root = root->_right;
			}
			else if (root->_right == NULL)
			{
				root = root->_left;
			}
			else
			{
				Node* parent = NULL;
				Node* firstleft = root->_right;
				while (firstleft->_left)
				{
					parent = firstleft;
					firstleft = firstleft->_left;
				}
				del = firstleft;

				swap(root->_key, firstleft->_key);
				swap(root->_value, firstleft->_value);

				if (parent && parent->_left == firstleft)
				{
					parent->_left = firstleft->_right;
				}
				else if (parent && parent->_right == firstleft)
				{
					parent->_right = firstleft->_right;
				}
				else //parent==NULL。待删除结点的右边只有一个结点,则最左结点就是它
				{
					root->_right = NULL;
				}
			}
			delete del;
			return true;
		}
		return false;
	}

          删除节点要考虑到的因素就要多了。我们可以 
划分子问题的方法来解决: 

     查找待删除的结点,每次判断:

  1. 当前结点key值大于key。递归进入左子树,继续查找。
  2. 当前结点key值小于key。递归进入右子树,继续查找。
  3. 当前结点key值等于key。在这又分为4种情况:
  • 当前结点的左子树为空。删除当前结点,把右子树给当前指针
  • 当前结点的右子树为空。删除当前结点,把左子树给当前指针
  • 当前结点的左右子树都为空。把根指针置空,删除当前结点。
  • 当前结点的左右子树都不为空。找到右子树的最左结点,和待删除结点交换值,删除最左结点。
             
把这些因素都考虑周全就可以准确的删除二叉搜索树的任何一个结点。


         


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值