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

二叉搜索树

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

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

结构定义及构造函数

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

### 3.1 非递归实现二叉搜索树的插入操作 在非递归方式中,插入操作的核心思想是通过指针逐层查找插入位置,并通过父节点链接新节点。插入逻辑是:从根节点出发,比较当前节点与插入的大小关系,决定向左子树或右子树移动,直到找到空指针位置插入新节点。 ```cpp template <typename K> void BSTree<K>::insert(const K& key) { Node<K>* newNode = new Node<K>(key); if (root == nullptr) { root = newNode; return; } Node<K>* cur = root; Node<K>* parent = nullptr; while (cur) { parent = cur; if (key < cur->key) { cur = cur->left; } else if (key > cur->key) { cur = cur->right; } else { // 已存在相同键,不插入 delete newNode; return; } } if (key < parent->key) { parent->left = newNode; } else { parent->right = newNode; } } ``` 该方法通过循环查找插入位置,避免了递归带来的栈开销,提高了执行效率[^4]。 --- ### 3.2 非递归实现二叉搜索树的遍历操作 二叉树的非递归遍历依赖栈结构模拟递归调用栈,以下为前序、中序后序的非递归实现。 #### 前序遍历(Preorder Traversal) ```cpp template <typename K> void BSTree<K>::preOrder() { std::stack<Node<K>*> stack; Node<K>* cur = root; while (cur || !stack.empty()) { while (cur) { std::cout << cur->key << " "; stack.push(cur); cur = cur->left; } cur = stack.top(); stack.pop(); cur = cur->right; } } ``` 该实现通过先访问节点再压栈,然后访问左子树,最后处理右子树的方式实现前序遍历[^1]。 #### 中序遍历(Inorder Traversal) ```cpp template <typename K> void BSTree<K>::inOrder() { std::stack<Node<K>*> stack; Node<K>* cur = root; while (cur || !stack.empty()) { while (cur) { stack.push(cur); cur = cur->left; } cur = stack.top(); stack.pop(); std::cout << cur->key << " "; cur = cur->right; } } ``` 中序遍历按照左根右的顺序访问节点,能够输出有序序列,适用于二叉搜索树的验证排序输出[^1]。 #### 后序遍历(Postorder Traversal) 后序遍历较为复杂,通常需要两个栈或记录访问状态: ```cpp template <typename K> void BSTree<K>::postOrder() { std::stack<Node<K>*> stack; Node<K>* cur = root; Node<K>* lastVisited = nullptr; while (cur || !stack.empty()) { while (cur) { stack.push(cur); cur = cur->left; } cur = stack.top(); if (cur->right == nullptr || cur->right == lastVisited) { std::cout << cur->key << " "; lastVisited = cur; stack.pop(); cur = nullptr; } else { cur = cur->right; } } } ``` 该实现通过记录已访问节点来判断是否回溯,确保左、右子树都被访问后才访问根节点[^1]。 --- ### 3.3 非递归实现二叉搜索树的删除操作 删除操作较为复杂,需要处理三种情况:节点为叶子节点、节点只有一个子节点、节点有两个子节点。非递归实现中,需要找到目标节点及其父节点,再根据子节点情况调整链接。 ```cpp template <typename K> void BSTree<K>::remove(const K& key) { Node<K>* cur = root; Node<K>* parent = nullptr; while (cur && cur->key != key) { parent = cur; if (key < cur->key) { cur = cur->left; } else { cur = cur->right; } } if (!cur) return; // 情况1:叶子节点 if (!cur->left && !cur->right) { if (cur == root) { root = nullptr; } else if (parent->left == cur) { parent->left = nullptr; } else { parent->right = nullptr; } delete cur; } // 情况2:只有左子树 else if (cur->left && !cur->right) { if (cur == root) { root = cur->left; } else if (parent->left == cur) { parent->left = cur->left; } else { parent->right = cur->left; } delete cur; } // 情况3:只有右子树 else if (!cur->left && cur->right) { if (cur == root) { root = cur->right; } else if (parent->left == cur) { parent->left = cur->right; } else { parent->right = cur->right; } delete cur; } // 情况4:左右子树都存在,找后继节点替换 else { Node<K>* successor = cur->right; Node<K>* sParent = cur; while (successor->left) { sParent = successor; successor = successor->left; } cur->key = successor->key; if (sParent->left == successor) { sParent->left = successor->right; } else { sParent->right = successor->right; } delete successor; } } ``` 该实现通过查找目标节点并处理其子节点情况,避免递归调用,提升了执行效率[^2]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值