二叉搜索树递归和非递归实现

二叉搜索树

又称二叉排序树,在进行中序遍历时可以进行排序+去重。

特征:若左树不为空,则左树的所有节点的值都小于根节点的值。若右树不为空,则右树的所有节点的值都大于根节点的值

结构定义及构造函数

template<class K>
struct BSTreeNode
{
	BSTreeNode<K>* _left;
	BSTreeNode<K>* _right;
	K _key;
	BSTreeNode(const K& key)
		:_left(nullptr)
		, _right(nullptr)
		, _key(key)
	{

	}
};
struct BSTree
{
	typedef BSTreeNode<K> Node;
public:
	BSTree()
		:_root(nullptr)
	{

	}

中序遍历

void InOrder()
	{
	 _InOrder(_root);
	 cout <<endl;
	}
void _InOrder(Node * root)
		{
			if (root == nullptr)
			{
				return;
			}
			_InOrder(root->_left);
			cout << root->_key << " ";
			_InOrder(root->_right);
		}

非递归法·

插入

bool Insert(const K& key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);
			return true;
		}
		Node* parent = nullptr;
		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;
			}
		}
		cur = new Node(key);
		if (parent->_key>key)
		{
			parent->_left = cur;
		}
		else if (parent->_key<key)
		{
			parent->_right = cur;
		}
		else
		{
			return false;
		}
		return true;
	}

总思路就是给一个父亲来记录上一个路径,一个cur来往下找合适插入位置,找到了就往左链接或者往右链接

查找

bool Find(const K& 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 Erase(const K& key)
	{
		Node* cur = _root;
		Node* parent = cur;
		while (cur)
		{
			if (cur->_key > key)
			{
				cur = cur->_left;
				parent = cur;
			}
			else if (cur->_key < key)
			{
				cur = cur->_right;
				parent = cur;
			}
			else
			{
//找到了,准备删除
				if (cur->_left == nullptr)
				{
					if (parent == nullptr)
					{
						_root = cur->_right;
					}
					if (parent->_left == cur)
					{
						parent->_left = cur->_right;
					}
					else
					{
						parent->_right = cur->_right;
					}
					delete cur;
				}
				else if (cur->_right == nullptr)
				{
					if (parent == nullptr)
					{
						_root = cur->_left;
					}
					if (parent->_left == cur)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
					delete cur;
				}
				else
				{
					Node* minparent = cur;
					Node* min = cur->_right;
					while (min->_left)
					{
						minparent = min;
						min = min->_left;

					}
					cur->_key = min->_key;
					if (minparent->_left == min)
					{
						minparent->_left = min->_right;//这里左右都可以,反正都是空
					}
					else
					{
						minparent->_right = min->_right;
					}
					delete min;
				}
				return true;
			}

			return false;
		}
	}

删除核心思想就是先找到要删除的点,分三种情况:1,没有孩子节点 2,有一个孩子节点

3,有两个孩子节点   

1和2情况都是直接删除,3情况是用替换法删除

当找到那个要删除的节点,1,2情况有两个大方向即:节点左子树为空和节点右子树为空

3情况就是找到值和记录值的父亲,最后判断值是大于记录值的父亲还是小于记录值的父亲来进行替换

递归法.

由于递归法是利用递归不断更新新子树的根节点来进行递归,所以要传root,但是root是私有的,所以我们要写一个子函数来调用从而达到可以传参root

	bool InsertR(const K& key)
		{
			return _InsertR(_root, key);
		}
		bool FindR(const K& key)
		{
			return _FindR(_root, key);
		}
		bool EraseR(const K& key)
		{
			return _EraseR(_root, key);
		}

插入

bool _InsertR(Node*& root, const K & 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;
			}
		}

这里传参root用了一个引用,递归时root也代表着root左右节点的指针

查找

Node* _FindR(Node * root, const K & 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 root;
			}
		}

删除

bool _EraseR(Node*& root, const K& 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->_right;
				}
				else
				{
					Node* min = del->_right;
					while (min->_left)
					{
						del = min;
						min = min->_left;
					}
					swap(min->_key, root->_key);
					return _EraseR(root->_right, key);//递归到右子树从右子树开始删
				}
				delete del;
				return true;
			}
		}

先找到要删除的节点,如果节点左右为空或者只有一个节点利用递归root也代表着root的左右节点的指针来进行删除。如果节点左右都不为空,那么老样子找到要删除的节点和替代节点,然后将两个值进行交换,交换后不构成搜索二叉数,但是它的根节点的左子树和右子树还是搜索二叉数,所以可以返回根节点的左子树和右子树来进行查找交换后的要删除的节点,从而进行删除

搜索二叉数的应用

key搜索模型

key搜索模型解决在不在的问题

	void TestBSTree1()
	{
		BSTree<string, string > dict;
		dict.InsertR("insert", "插入");
		dict.InsertR("sort", "排序");
		dict.InsertR("right", "右边");
		dict.InsertR("date", "日期");
		
		string str;
		while (cin>>str)
		{
			
			auto * ret  = dict.FindR(str);
			//auto ret = dict.FindR(str);
			if (ret)
			{
				cout << ret->_value << endl;
			}
			else
			{
				cout << "无此单词" << endl;
			}
		}
	}

key/value搜索模型

key/value模型解决通过一个值来查找另外一个值

void TestBSTree2()
	{
		// 统计水果出现次数
		string arr[] = { "苹果", "西瓜","草莓", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
		BSTree<string, int> countTree;
		for (auto& str : arr)
		{
			//BSTreeNode<string, int>* ret = countTree.Find(str);
			auto ret = countTree.Find(str);
			if (ret != nullptr)
			{
				ret->_value++;
			}
			else
			{
				countTree.Insert(str, 1);
			}
		}

		countTree.InOrder();
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值