【C++】实现一个搜索二叉树(BSTree):从定义到操作全解析


搜索二叉树(Binary Search Tree,简称BST)是一种特殊的二叉树,它满足任一节点的左子树中的所有节点的值都小于该节点的值,右子树中的所有节点的值都大于该节点的值。这使得BST在查找、插入和删除操作上具有较高的效率。本文将详细介绍如何从零开始实现一个搜索二叉树,包括其节点结构的定义和一系列基本操作的实现。

BSTreeNode类的定义

首先,我们需要定义树的节点BSTreeNode,它将作为树的基础结构:

template<class K, class V>
class BSTreeNode {
public:
    K _key;     // 节点存储的键
    V _value;   // 节点存储的值
    BSTreeNode* _left;   // 左子节点
    BSTreeNode* _right;  // 右子节点

    BSTreeNode(const K& key, const V& value)
        : _key(key), _value(value), _left(nullptr), _right(nullptr) {}
};

BSTree类的实现

接着,我们来实现BSTree类,它包含了一系列操作二叉搜索树的方法:

插入操作

插入操作Insert的核心思想是比较待插入节点的键与当前节点的键,决定向左子树还是右子树递归地进行插入操作。

	bool Insert(const K& key, const V& value) {
		if (!_root) {
			_root = new Node(key, value);
			return true;
		}

		Node* parent = nullptr;
		Node* current = _root;
		while (current) {
			parent = current;
			if (key < current->_key) {
				current = current->_left;
			}
			else if (key > current->_key) {
				current = current->_right;
			}
			else {
				return false; // key已经存在
			}
		}

		if (key < parent->_key) {
			parent->_left = new Node(key, value);
		}
		else {
			parent->_right = new Node(key, value);
		}

		return true;
	}

查找操作

查找操作Find通过逐层比较键值,沿树向下移动,直到找到匹配的节点或达到叶子节点。

Node* Find(const K& key) {
	Node* current = _root;
	while (current) {
		if (key < current->_key) {
			current = current->_left;
		}
		else if (key > current->_key) {
			current = current->_right;
		}
		else {
			return current; // 找到了
		}
	}
	return nullptr; // 没有找到
}

删除操作

删除操作Erase是最复杂的,它需要处理三种情况:被删除节点是叶子节点、只有一个子节点、有两个子节点。

  1. 被删除节点是叶子节点:直接删除该节点,并将其父节点的相应指针设置为nullptr
  2. 被删除节点只有一个子节点:删除该节点,并将其父节点的相应指针指向其子节点。
  3. 被删除节点有两个子节点:找到该节点的中序后继节点(右子树中的最小节点),用它来替换被删除节点的键和值,然后删除中序后继节点。

下面是Erase函数的实现代码:

bool Erase(const K& key) {
	Node* parent = nullptr;
	Node* current = _root;
	// 查找key对应的节点,同时记录其父节点
	while (current != nullptr && current->_key != key) {
		parent = current;
		if (key < current->_key) {
			current = current->_left;
		}
		else {
			current = current->_right;
		}

		if (current == nullptr) {
			// 没有找到对应的节点
			return false;
		}
	}
	//case1&2: 没有孩子或者只有一个孩子
	if (current->_left == nullptr || current->_right == nullptr) {
		Node* child = (current->_left == nullptr) ? current->_right : current->_left;
		if (parent == nullptr) {
			// 删除的是根节点
			_root = child;
		}
		else {
			if (parent->_left == current) {
				parent->_left = child;
			}
			else {
				parent->_right = child;
			}
		}
		delete current;
	}
	else {
		// case 3: 两个孩子
		// 找到右子树最小节点, 即中序后继节点
		Node* successor = current->_right;
		Node* successorParent = current;
		while (successor->_left != nullptr) {
			successorParent = successor;
			successor = successor->_left;
		}

		// 替换current 的节点和键值
		current->_key = successor->_key;
		current->_value = successor->_value;
		if (successorParent->_left = successor) {
			successorParent->_left = successor->_right;
		}
		else {
			successorParent->_right = successor->_right;
		}

		delete successor;
	}
}

中序遍历

中序遍历InOrder按照左子树-根节点-右子树的顺序访问树中的每个节点,对于BST来说,这将以升序方式访问所有节点。

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

递归实现

对于插入、查找和删除操作,我们还提供了递归版本的实现方法,以更直观地展示这些操作的逻辑。

// 递归版本 //
	bool FindR(const K& key) {
		return _FindR(_root, key);
	}

	bool InsertR(const K& key, const V& value) {
		return _InsertR(_root, key, value);
	}

	bool EraseR(const K& key) {
		return _EraseR(_root, key);
	}
private:
	bool _FindR(Node* root, const K& key) {
		if (root == nullptr)
			return false;

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

	bool _InsertR(Node*& root, const K& key, const V& value) {
		if (root == nullptr) {
			root = new Node(key, value);
			return true;
		}

		if (root->_key < key) {
			return _InsertR(root->_right, key, value);
		}
		else if (root->_key > key) {
			return _InsertR(root->_left, key, value);
		}
		else {
			return false;
		}
	}

	bool _EraseR(Node*& root, const K& key) {
		if (root == nullptr) {
			return false;
		}

		if (root->_key < key) {
			return _EraseR(root->_right, key);
		}
		else if (root->_key > key) {
			return _EraseR(root->_left, key);
		}
		else {
			//case1&2: 没有孩子或者只有一个孩子
			Node* del = root;
			if (root->_right == nullptr) {
				root = root->_left;
			} 
			else if (root->_left == nullptr){
				root = root->_right;
			}
			else {
				// case 3: 两个孩子
				// 找到右子树最小节点,即中序后继节点
				Node* successor = root->_right;
				while (successor->_left != nullptr) {
					successor = successor->_left;
				}

				// 将中序后继节点的值复制到当前节点
				root->_key = successor->_key;

				// 递归删除中序后继节点
				_EraseR(root->_right, successor->_key);
			}
			delete del;
			return true;
		}
	}

完整代码和测试

文章最后给出了BSTree类的完整实现代码,并提供了几个简单的测试用例来验证我们的实现。

#pragma once
#include <iostream>
#include <string>
using namespace std;

template<class K, class V>
class BSTreeNode {
public:
	K _key;	// 节点存储的键
	V _value;	// 节点存储的值
	BSTreeNode* _left; //左子节点
	BSTreeNode* _right; //右子节点

	BSTreeNode(const K& key, const V& value)
		: _key(key), _value(value), _left(nullptr), _right(nullptr) {}
};

template<class K, class V>
class BSTree
{
	typedef BSTreeNode<K, V> Node;
public:
	//强制生成默认构造
	BSTree() = default;

	BSTree(const BSTree<K,V>& t) {
		_root = Copy(t._root);
	}

	BSTree<K,V>& operator=(BSTree<K,V>& t) {
		swap(_root, t._root);
	}

	Node* Copy(Node* root) {
		if (root == nullptr) {
			return nullptr;
		}

		Node* newRoot = new Node(root->_key, root->_value);
		newRoot->_left = Copy(root->_left);
		newRoot->_right = Copy(root->_right);
		return newRoot;
	}

	~BSTree()
	{
		Destroy(_root);
	}

	void Destroy(Node* root) {
		if (root == nullptr)
			return;
		Destroy(root->_left);
		Destroy(root->_right);
		delete root;
	}

	bool Insert(const K& key, const V& value) {
		if (!_root) {
			_root = new Node(key, value);
			return true;
		}

		Node* parent = nullptr;
		Node* current = _root;
		while (current) {
			parent = current;
			if (key < current->_key) {
				current = current->_left;
			}
			else if (key > current->_key) {
				current = current->_right;
			}
			else {
				return false; // key已经存在
			}
		}

		if (key < parent->_key) {
			parent->_left = new Node(key, value);
		}
		else {
			parent->_right = new Node(key, value);
		}

		return true;
	}

	Node* Find(const K& key) {
		Node* current = _root;
		while (current) {
			if (key < current->_key) {
				current = current->_left;
			}
			else if (key > current->_key) {
				current = current->_right;
			}
			else {
				return current; // 找到了
			}
		}
		return nullptr; // 没有找到
	}
	bool Erase(const K& key) {
		Node* parent = nullptr;
		Node* current = _root;
		// 查找key对应的节点,同时记录其父节点
		while (current != nullptr && current->_key != key) {
			parent = current;
			if (key < current->_key) {
				current = current->_left;
			}
			else {
				current = current->_right;
			}

			if (current == nullptr) {
				// 没有找到对应的节点
				return false;
			}
		}
		//case1&2: 没有孩子或者只有一个孩子
		if (current->_left == nullptr || current->_right == nullptr) {
			Node* child = (current->_left == nullptr) ? current->_right : current->_left;
			if (parent == nullptr) {
				// 删除的是根节点
				_root = child;
			}
			else {
				if (parent->_left == current) {
					parent->_left = child;
				}
				else {
					parent->_right = child;
				}
			}
			delete current;
		}
		else {
			// case 3: 两个孩子
			// 找到右子树最小节点, 即中序后继节点
			Node* successor = current->_right;
			Node* successorParent = current;
			while (successor->_left != nullptr) {
				successorParent = successor;
				successor = successor->_left;
			}

			// 替换current 的节点和键值
			current->_key = successor->_key;
			current->_value = successor->_value;
			if (successorParent->_left = successor) {
				successorParent->_left = successor->_right;
			}
			else {
				successorParent->_right = successor->_right;
			}

			delete successor;
		}
	}

	void InOrder() {
		_InOrder(_root);
	}

	// 递归版本 //
	bool FindR(const K& key) {
		return _FindR(_root, key);
	}

	bool InsertR(const K& key, const V& value) {
		return _InsertR(_root, key, value);
	}

	bool EraseR(const K& key) {
		return _EraseR(_root, key);
	}
private:
	bool _FindR(Node* root, const K& key) {
		if (root == nullptr)
			return false;

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

	bool _InsertR(Node*& root, const K& key, const V& value) {
		if (root == nullptr) {
			root = new Node(key, value);
			return true;
		}

		if (root->_key < key) {
			return _InsertR(root->_right, key, value);
		}
		else if (root->_key > key) {
			return _InsertR(root->_left, key, value);
		}
		else {
			return false;
		}
	}

	bool _EraseR(Node*& root, const K& key) {
		if (root == nullptr) {
			return false;
		}

		if (root->_key < key) {
			return _EraseR(root->_right, key);
		}
		else if (root->_key > key) {
			return _EraseR(root->_left, key);
		}
		else {
			//case1&2: 没有孩子或者只有一个孩子
			Node* del = root;
			if (root->_right == nullptr) {
				root = root->_left;
			} 
			else if (root->_left == nullptr){
				root = root->_right;
			}
			else {
				// case 3: 两个孩子
				// 找到右子树最小节点,即中序后继节点
				Node* successor = root->_right;
				while (successor->_left != nullptr) {
					successor = successor->_left;
				}

				// 将中序后继节点的值复制到当前节点
				root->_key = successor->_key;

				// 递归删除中序后继节点
				_EraseR(root->_right, successor->_key);
			}
			delete del;
			return true;
		}
	}

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

void TestBSTree1()
{
	BSTree<string, string> dict;
	dict.InsertR("insert", "插入");
	dict.InsertR("erase", "删除");
	dict.InsertR("left", "左边");
	dict.InsertR("string", "字符串");
	dict.InOrder();
	string str;
	while (cin >> str)
	{
		auto ret = dict.Find(str);
		if (ret)
		{
			cout << str << ":" << ret->_value << endl;
		}
		else
		{
			cout << "单词拼写错误" << endl;
		}
		cout << "删除: " << ret->_key << " : " << ret->_value << endl;
		dict.EraseR(ret->_key);
		dict.InOrder();
	}
}

void TestBSTree2()
{
	string strs[] = { "苹果", "西瓜", "苹果", "樱桃", "苹果", "樱桃", "苹果", "樱桃", "苹果" };
	// 统计水果出现的次
	BSTree<string, int> countTree;
	for (auto str : strs)
	{
		auto ret = countTree.Find(str);
		if (ret == NULL)
		{
			countTree.Insert(str, 1);
		}
		else
		{
			ret->_value++;
		}
	}
	countTree.InOrder();
}

void TestBSTree3() {
	BSTree<int, string> t;
	t.Insert(8, "八");
	t.Insert(3, "三");
	t.Insert(1, "一");
	t.Insert(10, "十");
	t.Insert(6, "六");
	t.Insert(4, "四");
	t.Insert(7, "七");
	t.Insert(14, "十四");
	t.Insert(13, "十三");
	
	t.InOrder();

	BSTree<int, string> t1(t);
	t1.InOrder();
}

总结

通过本文的介绍,我们了解了二叉搜索树的基本概念、节点结构的定义以及如何实现插入、查找、删除和中序遍历等基本操作。实现一个搜索二叉树不仅能够加深对数据结构和算法的理解,也能够提升编程能力和问题解决能力。

  • 16
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Q_hd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值