数据结构搜索二叉树的(递归与非递归)实现,包括:增Insert,删Remove,查Find

Posted hanjing_1995

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构搜索二叉树的(递归与非递归)实现,包括:增Insert,删Remove,查Find相关的知识,希望对你有一定的参考价值。

搜索二叉树,是二叉树一种特殊的结构。

特点:

(1)每个节点都有一个关键码,并且关键码不重复。

(2)左子树上的每个节点的关键码都小于根节点的关键码。

(3)右子树上的每个节点的关键码都大于根节点的关键码。

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


下面,为方便大家理解,我举例画一个搜索二叉树,如下:


代码见下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;

//搜索二叉树的节点结构
template<class K,class V>
struct SearchBinaryNode
{
	SearchBinaryNode* _left;//左子树
	SearchBinaryNode* _right;//右子树
	K _key;//每个节点是(_key,_value)形式
	V _value;

	//搜索二叉树的节点的构造函数
	SearchBinaryNode(const K& key, const V& value)
		:_left(NULL)
		, _right(NULL)
		, _key(key)
		, _value(value)
	{}
};


template<class K,class V>
class SearchBinaryTree
{
	typedef SearchBinaryNode<K, V> Node;
public:
	//搜索二叉树的构造函数
	SearchBinaryTree()
		:_root(NULL)
	{}

	//给搜索二叉树插入一个节点
	bool Insert(const K& key, const V& value)
	{
		//搜索二叉树无节点,直接挂上该节点
		if (_root == NULL)
		{
			_root = new Node(key, value);
			return true;
		}

		//遍历搜索二叉树,要插入的节点比每个根节点小,往左子树走,否则大往右子树走
		Node* cur = _root;
		Node* prev = NULL;//保存节点,便于插入节点链在上面
		while (cur)
		{
			if (key < cur->_key)
			{
				prev = cur;
				cur = cur->_left;
			}
			else if (key > cur->_key)
			{
				prev = cur;
				cur = cur->_right;
			}
			else
			{
				return false;//搜索二叉树要求无重复关键码
			}
		}
		
		//此时不知道链在左子树还是右子树,分情况
		if (prev->_key > key)
		{
			prev->_left = new Node(key, value);
		}
		else if (prev->_key < key)
		{
			prev->_right = new Node(key, value);
		}
		return true;
	}


	//寻找搜索二叉树的一个节点
	bool Find(const K& key,const V& value)
	{
		if (_root == NULL)
		{
			return false;
		}
		Node* cur = _root;
		while (cur)
		{
			if (key < cur->_key)
			{
				cur = cur->_left;
			}
			else if (key > cur->_key)
			{
				cur = cur->_right;
			}
			else
			{
				return true;
			}
		}
		return true;
	}


	//删除搜索二叉树的一个节点
	bool Remove(const K& key,const V& value)
	{
		//无结点:返回
		if (_root == NULL)
		{
			return false;
		}

		//一个节点:直接删
		else if (_root->_left == NULL && _root->_right == NULL)
		{
			if (_root->_key == key)
			{
				delete _root;
				_root = NULL;
				return true;
			}
			else
				return false;
		}

		//多个节点
		else
		{
			Node* cur = _root;
			Node* parent = NULL;
			while (cur)
			{
				//往左子树上走去找
				if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}

				//往右子树上走去找
				else if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}

				//找到该节点,确定如何去删除该节点
				else
				{
					//要删除的节点无左子树
					if (cur->_left == NULL)
					{
						//问题:这时候要处理一个特殊情况,如果删到只剩下根节点parent:5,以及它的右子树parent->_right:9时,parent为空,
						//访问parent的成员会导致程序崩溃
						//解决:parent为空时,则将parent->right置成根_root,删掉parent节点
						if (parent == NULL)
						{
							_root = cur->_right;
							delete cur;
							cur = NULL;
							return true;
						}
						
						//两种情况:
						//(1)删除的cur在parent的右子树上时,cur->_right链在parent->_right上					
						if (parent->_key<cur->_key)
						{
							parent->_right = cur->_right;
						}

						//(2)否则,cur->_right链在parent->_left
						else if (parent->_key>cur->_key)
						{
							parent->_left = cur->_right;
						}
						delete cur;
						cur = NULL;
						return true;
					}

					//要删除的节点无右子树
					else if (cur->_right == NULL)
					{
						//同cur->_left为空
						if (parent == NULL)
						{
							_root = cur->_left;
							delete cur;
							cur = NULL;
							return true;
						}

						//两种情况:
						//(1)删除的cur在parent的右子树上时,cur->_left链在parent->_right上
						if (parent->_key < cur->_key)
						{
							parent->_right = cur->_left;							
						}

						//(2)否则,cur->_left链在parent->_left
						else if (parent->_key > cur->_key)
						{
							parent->_left = cur->_left;
						}

						//删除释放
						delete cur;
						cur = NULL;
						return true;
					}

					//左右子树都不为空,分两种情况
					else
					{
						//firstLeft是要删除的节点cur的右子树(代码走到这一步,说明左右必然不为空,则肯定不为空)
						Node* firstLeft = cur->_right;

						//往firstLeft的左子树上走,找最左节点
						//(1)左为空,交换,把链在firstLeft的一串节点(仅有右子树)链在cur->_right上
						if (firstLeft->_left == NULL)
						{						
							swap(cur->_key, firstLeft->_key);
							swap(cur->_value, firstLeft->_value);
							cur->_right = firstLeft->_right;
							delete firstLeft;
							firstLeft = NULL;
							return true;
						}

						//左为空,一直往其左子树上走,找到,交换
						else
						{
							Node* tmp = firstLeft;

							//tmp为最左节点
							while (tmp->_left)
							{
								firstLeft = tmp;
								tmp = tmp->_left;
							}
							swap(cur->_key, tmp->_key);
							swap(cur->_value, tmp->_value);

							//把链在tmp的一串节点(仅有右子树)链在firstLeft->_right上
							firstLeft->_left = tmp->_right;
							delete tmp;
							tmp = NULL;
							return true;
						}
					}
				}
			}
		}
	}


	//调用_Insert_R
	void Insert_R(const K& key, const V& value)
	{
		_Insert_R(_root, key, value);
	}


	//调用_Find_R
	Node* Find_R(const K& key, const V& value)
	{
		Node* ret = _Find_R(_root, key, value);
		return ret;
	}
	

	//调用_Remove_R
	void Remove_R(const K& key, const V& value)
	{
		_Remove_R(_root,key, value);
	}
	//调用_InOrder
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

protected:

	//中序遍历打印:便于查看是否构造搜索二叉树是否正确
	void _InOrder(Node* root)
	{
		if (root == NULL)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_key << "  ";
		_InOrder(root->_right);		
	}


	//给搜索二叉树插入一个节点的递归写法
	/*注意:递归写法传引用,创建的节点要放在_root上,而不传引用的话,相当于在_root上拷贝
	的临时形参变量root上建了个节点,对_root本身没有作用*/
	void _Insert_R(Node*& root, const K& key, const V& value)
	{
		if (root == NULL)
		{
			root = new Node(key, value);
			return;
		}
		if (root->_key < key)//往右子树上递归
		{
			_Insert_R(root->_right, key, value);
		}
		else if (root->_key > key)//往左子树上递归
		{
			_Insert_R(root->_left, key, value);
		}
		else//搜索二叉树要求无重复关键码
			return;
	}


	//寻找搜索二叉树的一个节点的递归写法
	Node* _Find_R(Node* root, const K& key, const V& value)
	{
		if (root == NULL)
		{
			return NULL;
		}
		if (root->_key < key)//往右子树上递归
		{
			return _Find_R(root->_right, key, value);
		}
		else if (root->_key > key)//往左子树上递归
		{
			return _Find_R(root->_left, key, value);
		}
		else 
			return root;//找到,返回节点指针		
	}


	//删除搜索二叉树的一个节点的递归写法
	void _Remove_R(Node*& root, const K& key, const V& value)
	{
		//空节点,返回
		if (root == NULL)
			return;	

		//一个节点:若为该节点,删除。否则不处理
		if (root->_left == NULL && root->_right == NULL)
		{
			if (root->_key == key)
			{
				delete root;
				root = NULL;
				return;
			}
			else
				return;		
		}

		//往左子树上递归寻找
		if (root->_key > key)
		{
			_Remove_R(root->_left, key, value);
		}

		//往右子树上递归寻找
		else if (root->_key < key)
		{
			_Remove_R(root->_right, key, value);
		}

		//找到该节点,且该节点的左右子树不空,确定如何去删除该节点
		else
		{
			Node* del = NULL;

			//右子树为空,此时不需和非递归写法一样分两种情况(parent->_right是否)分别去处理,这就是递归的优势所在。
			//parent->_right为空,上一层递归来的是root->_left,即此时的root,链在root->_left上
			//parent->_right不为空时,上一层递归来的root->_right,即此时的root,链在root->_left上
			//所以,当parent->_right为空或者不为空时,均是上一层递归而来,root->_left或者root->_right,都可以用此时的root表示
			if (root->_right == NULL)
			{
				del = root;
				root = root->_left;
				delete del;
				del = NULL;
			}

			//同上,parent->_left为空时,上层递归来的root->_right,即此时的root,链在root->_right上
			//parent->_right不为空时,上层递归来的root->_left,是此时的root,链在root->_right上
			//而这两种情况,都可以用递归解决,无需分别处理
			else if (root->_left == NULL)
			{
				del = root;
				root = root->_right;
				delete del;
				del = NULL;
			}

			//左右子树都不为空,若不递归的话,要分两种情况处理。要删除的root节点要先找到右子树节点firstLeft,
			//再往firstLeft的左子树走,去找最左节点,此时可能左子树为空或者不为空。借用递归的优势,不管是上层的左还是右子树传过来的,
			//都是上一层递归来的,即都是此时的root.一起处理掉。
			//所以,这两种情况合在一起,可以
			else
			{
				Node* firstLeft = root->_right;	
				Node* tmp = firstLeft;

				//找最左节点
				while (tmp->_left)
				{
					firstLeft = tmp;
					tmp = tmp->_left;
				}

				//找到最左边节点,保存,交换,删除
				swap(tmp->_key, root->_key);
				swap(tmp->_value, root->_value);
				del = root;
				root = tmp->_right;
				delete del;
				del = NULL;			
			}
		}
	}

protected :
	Node* _root;
};


void TestSearchBinaryTree()
{
	typedef SearchBinaryNode<int, int> Node;
	SearchBinaryTree<int, int> sbt;
	sbt.Insert(5, 1);
	sbt.Insert(3, 1);
	sbt.Insert(2, 1);
	sbt.Insert(6, 1);
	sbt.Insert(0, 1);
	sbt.Insert(9, 1);
	sbt.Insert(7, 1);
	sbt.Insert(8, 1);
	sbt.Insert(4, 1);
	sbt.Insert(1, 1);
	sbt.InOrder();

	sbt.Find(3,1);

	//删除的完整测试用例:将上序插入的序列删除顺序打乱,并全部删除,所有删除情况基本都可涵盖。
	sbt.Remove(1, 1);
	sbt.Remove(3, 1);
	sbt.Remove(4, 1);	
	sbt.Remove(7, 1);
	sbt.Remove(6, 1);
	sbt.Remove(0, 1);
	sbt.Remove(8, 1);
	sbt.Remove(2, 1);
	sbt.Remove(5, 1);
	sbt.Remove(9, 1);
	sbt.InOrder();

	sbt.Insert_R(5, 1);
	sbt.Insert_R(3, 1);
	sbt.Insert_R(2, 1);
	sbt.Insert_R(6, 1);
	sbt.Insert_R(0, 1);
	sbt.Insert_R(9, 1);
	sbt.Insert_R(7, 1);
	sbt.Insert_R(8, 1);
	sbt.Insert_R(4, 1);
	sbt.Insert_R(1, 1);
	sbt.InOrder();
	
	Node* ret = sbt.Find_R(3, 1);
	if (ret == NULL)
	{
		cout << "not exist!" << endl;
	}
	else
		cout << ret->_key << endl;

	sbt.Remove_R(1, 1);
	sbt.Remove_R(3, 1);
	sbt.Remove_R(4, 1);
	sbt.Remove_R(7, 1);
	sbt.Remove_R(6, 1);
	sbt.Remove_R(0, 1);
	sbt.Remove_R(8, 1);
	sbt.Remove_R(2, 1);
	sbt.Remove_R(5, 1);
	sbt.Remove_R(9, 1);
	sbt.InOrder();
}

int main()
{
	TestSearchBinaryTree();
	system("pause");
	return 0;
}



以上是关于数据结构搜索二叉树的(递归与非递归)实现,包括:增Insert,删Remove,查Find的主要内容,如果未能解决你的问题,请参考以下文章

二叉树的遍历--递归实现与非递归实现

遍历二叉树的递归与非递归代码实现

Java数据结构——二叉树的递归与非递归遍历(DFS)

二叉树详解及二叉树的遍历(递归与非递归C++算法实现)

二叉树之AVL树的平衡实现(递归与非递归)

C++实现二叉树 前中后序遍历(递归与非递归)非递归实现过程最简洁版本