搜索二叉树

搜索二叉树的实现

框架和insert函数

我们很容易写出下面的框架和insert函数

struct BSTreeNode
{
	BSTreeNode(const K& val)
	{
		_val = val;
		_left = nullptr;
		_right = nullptr;
	}
	BSTreeNode<K>* _left;
	BSTreeNode<K>* _right;
	K _val;
};

template <class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
public:
	bool insert(const K& val)
	{
		// a. 树为空,则直接新增节点,赋值给root指针
		if (_root == nullptr) {
			_root = new BSTreeNode(val);
			return true;
		}
		// b. 树不空,按二叉搜索树性质查找插入位置,插入新节点
		Node* cur = _root, *prev=nullptr;
		while (cur) {
			if (val < cur->_val) {
				prev = cur;
				cur = cur->_left;
			}
            else if (val > cur->_val) {
				prev = cur;
				cur = cur->_right;
			}
			else {
				// val的值和树中某个节点的值相同,返回false
				return false;
			}
		}
		// 找到了一个合适的位置,此时需要链接,注意链接位置
		cur = new Node(val);
		if (val < prev->_val) {
			prev->_left = cur;
			return true;
		}
		else {
			prev->_right = cur;
			return true;
		}
	}
private:
	Node _root = nullptr;
};

中序遍历

我们知道,搜索二叉树的中序遍历是一个有序表,现在我们想要像这样调用

int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
BSTree<int> tree;
for (auto& e : a)
	tree.insert(e);
tree.InOrder();

但是InOrder()需要一个参数root,现在有三种解决办法,a.将_root改为public,b.写一个GetRoot(),c.如下

protected:
void _InOrder(Node* root)
{
	if (root == nullptr)	return;
	_InOrder(root->_left);
	cout << root->_val << ' ';
	_InOrder(root->_right);
}
public:
void InOrder()
{
	_InOrder(_root);
}

有人可能会这样写void _InOrder(Node* root) = _root, 这样是错误的,因为函数的默认参数只能是全局变量或者常量

find(const K& val)

bool find(const K& val)
{
	Node* cur = _root;
	while (cur) {
		if (val < cur->_val)
			cur = cur->_left;
		else if (val > cur->_val)
			cur = cur->_right;
		else
			return true;
	}
	return false;
}

erase()函数

image-20240513120013255

其中第②种情况包含第①中情况

bool erase(const K& key)
{
	Node* cur = _root, * prev = nullptr;
	while (cur) {
		if (key < cur->_key) {
			prev = cur;
			cur = cur->_left;
		}
		else if (key > cur->_key) {
			prev = cur;
			cur = cur->_right;
		}
		else {
			// 1.左为空,prev指向cur的_right,但是prev的_left还是_right需要判断
			if (cur->_left == nullptr) {
				if (prev->_left == cur)	prev->_left = cur->_right;
				else prev->_right = cur->_right;
				delete cur;
			}
			// 2.右为空
			else if (cur->_right == nullptr) {
				if (prev->_left == cur)	prev->_left = cur->_left;
				else prev->_right = cur->_left;
				delete cur;
			}
			// 其他情况,需要"找保姆"
			else {
				// 找右树的最小节点
				Node* minRight = cur->_right, * prevMinRight = cur;
				while (minRight->_left) {
					prevMinRight = minRight;
					minRight = minRight->_left;
				}
				cur->_key = minRight->_key;
				/*if (prevMinRight == cur) prevMinRight->_right = minRight->_right;
				else prevMinRight->_left = minRight->_right;*/
				// 这里也需要判断,因为不能保证prevMinRight是否是cur
				if (prevMinRight->_right == minRight) prevMinRight->_right = minRight->_right;
				else prevMinRight->_left = minRight->_right;
				delete minRight;
			}
			return true;
		}
	}
	return false;
}

看似上面的代码没有问题,但面对下面的测试用例时,就会有问题

// 测试erase
void test2()
{
	int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
	BSTree<int> tree;
	for (auto& e : a)
		tree.insert(e);
	tree.erase(10);
	tree.erase(14);
	tree.erase(13);
    tree.erase(8);
	tree.InOrder();
}

image-20240520175920684

此时有两种解决办法,一是更新_root,更新为节点3,二是找左子树的最大值来作为cur的prev
左树为空时同理

此时erase变为

bool erase(const K& key)
{
	Node* cur = _root, * prev = nullptr;
	while (cur) {
		if (key < cur->_key) {
			prev = cur;
			cur = cur->_left;
		}
		else if (key > cur->_key) {
			prev = cur;
			cur = cur->_right;
		}
		else {
			// 1.左为空,prev指向cur的_right,但是prev的_left还是_right需要判断
			if (cur->_left == nullptr) {
				if (cur == _root)	_root = cur->_right;
				else {
					if (prev->_left == cur)	prev->_left = cur->_right;
					else prev->_right = cur->_right;
					delete cur;
				}
			}
			// 2.右为空
			else if (cur->_right == nullptr) {
				if (cur == _root) _root = cur->_left;
				else {
					if (prev->_left == cur)	prev->_left = cur->_left;
					else prev->_right = cur->_left;
					delete cur;
				}
			}
			// 其他情况,需要"找保姆"
			else {
				// 找右树的最小节点
				Node* minRight = cur->_right, * prevMinRight = cur;
				while (minRight->_left) {
					prevMinRight = minRight;
					minRight = minRight->_left;
				}
				cur->_key = minRight->_key;
				/*if (prevMinRight == cur) prevMinRight->_right = minRight->_right;
				else prevMinRight->_left = minRight->_right;*/
				// 这里也需要判断,因为不能保证prevMinRight是否是cur
				if (prevMinRight->_right == minRight) prevMinRight->_right = minRight->_right;
				else prevMinRight->_left = minRight->_right;
				delete minRight;
			}
			return true;
		}
	}
	return false;
}

搜索二叉树的递归实现

findR()函数

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

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

insertR()函数

bool insertR(const K& key)
{
    return _insertR(_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->_right, key);
    else if (root->_key > key)	return _insertR(root->_left, key);
    else return false;
}

关于参数为什么是Node*& root,可以看下面的递归展开图

image-20240521105848190

eraseR()函数

bool eraseR(const K& key)
{
	return _eraseR(_root, key);
}

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 {
		Node* del = root;
		if (root->_left == nullptr) root = root->_right;
		else if (root->_right == nullptr) root = root->_left;
		else {
			// 找左子树的最大节点
			Node* maxLeft = root->_left;
			while (maxLeft->_right != nullptr) maxLeft = maxLeft->_right;
			swap(root->_key, maxLeft->_key);
			// 转换成子问题去删除
			return _eraseR(root->_left, key);
		}
		delete del;
		return true;
	}
}

注意第16行return _eraseR(root->_left, key);不能改成return _eraseR(maxLeft, key);,因为我们要想让Node*& root这个引用起作用,就需要一层一层的往下递归传递

其他函数

析构函数

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

拷贝构造函数

BSTree(const BSTree<K>& t)
{
	_root = Copy(t._root);
}
Node* Copy(Node* root)
{
	if (root == nullptr)	return nullptr;
	Node* newRoot = new Node(root->_key);
	newRoot->_left = Copy(root->_left);
	newRoot->_right = Copy(root->_right);

	return newRoot;
}

赋值函数重载

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

全部代码

#pragma once
#include <iostream>
using namespace std;
template <class K>
struct BSTreeNode
{
	BSTreeNode(const K& key)
	{
		_key = key;
		_left = nullptr;
		_right = nullptr;
	}
	BSTreeNode<K>* _left;
	BSTreeNode<K>* _right;
	K _key;
};

template <class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
public:
	~BSTree()
	{
		Destroy(_root);
		_root = nullptr; 
	}

	// BSTree() : _root(nullptr) {}
	// 等价于上面的无参构造函数
	BSTree() = default;

	BSTree(const BSTree<K>& t)
	{
		_root = Copy(t._root);
	}
	
	BSTree<K>& operator=(BSTree<K> t)
	{
		swap(t._root, _root);
		return *this;
	}

	bool insert(const K& key)
	{
		// a. 树为空,则直接新增节点,赋值给root指针
		if (_root == nullptr) {
			_root = new Node(key);
			return true;
		}
		// b. 树不空,按二叉搜索树性质查找插入位置,插入新节点
		Node* cur = _root, * prev = nullptr;
		while (cur) {
			if (key < cur->_key) {
				prev = cur;
				cur = cur->_left;
			}
			else if (key > cur->_key) {
				prev = cur;
				cur = cur->_right;
			}
			else {
				// key的值和树中某个节点的值相同,返回false
				return false;
			}
		}
		// 找到了一个合适的位置,此时需要链接,注意链接位置
		cur = new Node(key);
		if (key < prev->_key) {
			prev->_left = cur;
			return true;
		}
		else {
			prev->_right = cur;
			return true;
		}
	}

	bool insertR(const K& key)
	{
		return _insertR(_root, key);
	}
	
	bool find(const K& key)
	{
		Node* cur = _root;
		while (cur) {
			if (key < cur->_key)
				cur = cur->_left;
			else if (key > cur->_key)
				cur = cur->_right;
			else
				return true;
		}
		return false;
	}

	bool findR(const K& key)
	{
		return _findR(_root, key);
	}
	
	bool erase(const K& key)
	{
		Node* cur = _root, * prev = nullptr;
		while (cur) {
			if (key < cur->_key) {
				prev = cur;
				cur = cur->_left;
			}
			else if (key > cur->_key) {
				prev = cur;
				cur = cur->_right;
			}
			else {
				// 1.左为空,prev指向cur的_right,但是prev的_left还是_right需要判断
				if (cur->_left == nullptr) {
					if (cur == _root)	_root = cur->_right;
					else {
						if (prev->_left == cur)	prev->_left = cur->_right;
						else prev->_right = cur->_right;
						delete cur;
					}
				}
				// 2.右为空
				else if (cur->_right == nullptr) {
					if (cur == _root) _root = cur->_left;
					else {
						if (prev->_left == cur)	prev->_left = cur->_left;
						else prev->_right = cur->_left;
						delete cur;
					}
				}
				// 其他情况,需要"找保姆"
				else {
					// 找右树的最小节点
					Node* minRight = cur->_right, * prevMinRight = cur;
					while (minRight->_left) {
						prevMinRight = minRight;
						minRight = minRight->_left;
					}
					cur->_key = minRight->_key;
					/*if (prevMinRight == cur) prevMinRight->_right = minRight->_right;
					else prevMinRight->_left = minRight->_right;*/
					// 这里也需要判断,因为不能保证prevMinRight是否是cur
					if (prevMinRight->_right == minRight) prevMinRight->_right = minRight->_right;
					else prevMinRight->_left = minRight->_right;
					delete minRight;
				}
				return true;
			}
		}
		return false;
	}

	bool eraseR(const K& key)
	{
		return _eraseR(_root, key);
	}
	
	void InOrder()
	{
		_InOrder(_root);
	}
protected:
	void _InOrder(Node* root)
	{
		if (root == nullptr)	return;
		_InOrder(root->_left);
		cout << root->_key << ' ';
		_InOrder(root->_right);
	}

	bool _insertR(Node*& root, const K& key)
	{
		if (root == nullptr) {
			root = new Node(key);
			return true;
		}
		if (root->_key < key) return _insertR(root->_right, key);
		else if (root->_key > key)	return _insertR(root->_left, key);
		else return false;
	}

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

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

	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 {
			Node* del = root;
			if (root->_left == nullptr) root = root->_right;
			else if (root->_right == nullptr) root = root->_left;
			else {
				// 找左子树的最大节点
				Node* maxLeft = root->_left;
				while (maxLeft->_right != nullptr) maxLeft = maxLeft->_right;
				swap(root->_key, maxLeft->_key);
				// 转换成子问题去删除
				return _eraseR(root->_left, key);
			}
			delete del;
			return true;
		}
	}

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

	Node* Copy(Node* root)
	{
		if (root == nullptr)	return nullptr;
		Node* newRoot = new Node(root->_key);
		newRoot->_left = Copy(root->_left);
		newRoot->_right = Copy(root->_right);

		return newRoot;
	}
private:
	Node* _root = nullptr;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值