二叉搜索树BSTree 模拟实现

二叉搜索树:

这里实现两个版本 一个是val版本 一个是key-val版本

树节点

val版本:

template<class val_type>
struct BSTreeNode
{
	typedef BSTreeNode<val_type> Node;

	Node* _left;
	Node* _right;
	val_type _key;

	BSTreeNode(const val_type& key)
	:_key(key)
	,_left(nullptr)
	,_right(nullptr)
	{}
};

key-val版本:

template<class key_type ,class val_type>
struct BSTreeNode
{
	typedef BSTreeNode<key_type ,val_type> Node;

	Node* _left;
	Node* _right;
	val_type _val;
	key_type _key;

	BSTreeNode(const key_type& key , const val_type& val = val_type())
		:_key(key)
		,_val(val)
		, _left(nullptr)
		, _right(nullptr)
	{}
};

 二叉搜索树,首先满足二叉树,节点中存左孩子指针右孩子指针,然后存val 或者key-val。

初始化,先将节点指针初始化位空,再将传入的值赋key 和 val 这里 key-val版本中如果只传一个key的话,val默认调用val_type 的构造函数进行初始化

成员成员函数

key版本

template<class val_type>
class BSTree
{
public:
    typedef BSTreeNode<val_type> Node;
private:
	Node* _root = nullptr;
};

key-val版本

template<class key_type, class val_type>
class BSTree
{
public:
	typedef BSTreeNode<key_type, val_type> Node;
private:
	Node* _root = nullptr;
};

区别就是一个模板只用传一个参数,一个模板需要传两个参数 默认的根节点都初始化成nullptr

构造函数

BSTree() = default;

声明根节点的时候已经进行了nullptr初始化,所以无需再进行初始化 

拷贝构造

BSTree(const BSTree<val_type>& sorce)
{
	_root = copy(sorce._root);
}

BSTree<val_type> operator = (BSTree<val_type> sorce)
{
	swap(_root, sorce._root);
	return *this;
}
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;
}

拷贝构造两个版本几乎没有任何区别 

注意这里的copy函数要放在private里 不对外界开放使用

这里的operator 只进行了指针的拷贝,并没有拷贝整颗搜索树!!!

析构

~BSTree()
{
	Destroy(_root);
}
void Destroy(Node* root)
{
	if (root == nullptr)
		return;

	Destroy(root->_left);
	Destroy(root->_right);
	delete root;
}

这里的Destroy也建议放到private里,不对外界开放使用

这里进行递归析构后续遍历二叉树)因为被释放了之后就找不到leftright了,所以应该先释放孩子,在释放parent

两份版本的析构没有任何区别

查找

key版本:

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

 key-val版本:

Node* find(const key_type& key)
{
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key > key)
		{
			cur = cur->_left;
		}
		else if (cur->_key < key)
		{
			cur = cur->_right;
		}
		else
			return cur;
	}
	return nullptr;
}

原理:由于二叉搜索树的数据特点,左孩子比自己小,右孩子比自己大,所以比自己大向右走,比自己小向左走如果相同就说明找到了;

差别:这里key-val版本中传参与key一样,只用key来找整个节点,如果找到了返回节点的引用,与key版本区别的是,如果找到了只需要返回true(一般key都是不可以修改的,所以返回节点的引用没有意义)

插入

 key版本

bool insert(const val_type& 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->_right;
		}
		else if (cur->_key > key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
			return false;
	}

	cur = new Node(key);
	if (parent->_key > key)
		parent->_left = cur;
	else
		parent->_right = cur;

	return true;
}

 key-val版本

bool insert(const key_type& key,const val_type& val = val_type())
{
	if (_root == nullptr)
	{
		_root = new Node(key,val);
		return true;
	}

	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key < key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_key > key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
			return false;
	}

	cur = new Node(key,val);
	if (parent->_key > key)
		parent->_left = cur;
	else
		parent->_right = cur;

	return true;
}

插入元素比较简单,首先判断根节点是不是为空如果为空则根节点指向新节点,若不为空,则需要先找到新节点在整个二叉搜索树的合适位置,遍历到叶子节点,根据与叶子节点的内容大小判断插入到叶子节点的左还是右。

这里两个版本的插入没有什么太大的区别,主要还是传参不同,这里key-val版本的val默认调用val_type的构造函数进行初始化

删除

删除的情况比较多需要分多种情况考虑,但是两个版本的区别几乎没有(不需要用到val) 

这里用key版本举例

bool erase(const key_type& key)
{
	Node* parent = nullptr;
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key < key)//先找到节点
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_key > key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else//cur->_key==key
		{
			if (cur->_left == nullptr)//左节点为空
			{
				if (cur == _root)
					_root = _root->_right;
				else
				{
					if (cur == parent->_left)//cur位parent的左节点
						parent->_left = cur->_right;

					else//cur是parent的右节点
						parent->_right = cur->_right;
				}
				delete cur;//清理要删除的节点
				cur = nullptr;
				return true;
			}
			else if (cur->_right == nullptr)
			{
				if (cur == _root)
					_root = _root->_right;
				else
				{
					if (cur == parent->_left)//cur位parent的左节点
						parent->_left = cur->_left;
					else//cur是parent的右节点
						parent->_right = cur->_left;
				}

				delete cur;//清理要删除的节点
				cur = nullptr;
				return true;
			}
			else//cur 左右节点都不为空
			{
				//找到与cur差距最小的节点
				Node* rightMinParent = cur;
				Node* rightMin = cur->_right;
				while (rightMin->_left)//找到cur->_right的最左节点
				{
					rightMinParent = rightMin;
					rightMin = rightMin->_left;
				}

				cur->_key = rightMin->_key;//交换两个节点的值,让rightMin替换cur
				//加一层判断
				if (rightMin == rightMinParent->_left)
					rightMinParent->_left = rightMin->_right;
				else
					rightMinParent->_right = rightMin->_right;

				delete rightMin;
				rightMin = nullptr;
				return true;
			}
		}
	}
	return false;
}

 首先还是找到该节点,如果没有找到则不需要删除,返回false即可,如果需要删除,返回true

接下来看删除的具体情况分类:(要删除的节点找到了,为cur)

注:圆形表示节点,方形表示子树 

1.

if (cur->_left == nullptr)

 1-1

if (cur == _root)

这种情况下,要删除cur节点的话,直接让  _root = _root->_right 完成删除!

1-2

if(cur != _root)

 这两种情况:比较简单,由于cur的left为空,所以直接将cur的右子树,给parent的left或right即可

if (cur == parent->_left)//cur位parent的左节点
	parent->_left = cur->_right;
else//cur是parent的右节点
	parent->_right = cur->_right;

 2

if (cur->_right == nullptr)

 这种情况与第一种情况分析相同:

2-1:

if (cur == _root)

 

这种情况下,要删除cur节点的话,直接让  _root = _root-> _left 完成删除! 

2-2

if(cur != _root)

 

这两种情况:比较简单,由于curright 为空,所以直接将cur左子树,给parent的left或right即可

if (cur == parent->_left)//cur位parent的左节点
		parent->_left = cur->_left;
else//cur是parent的右节点
		parent->_right = cur->_left;

 3:cur 的left和right 都不为空 这种情况最复杂!

我们需要找到合适的元素来替换掉cur节点来完成删除cur的目的,首要是要满足替换完之后整体还要满足二叉搜索树的特点,我们只需要找到cur->left 节点 cur->right节点之间的值就可以了,我们可以把cur->right 节点的最左节点 或者cur->left 节点的最右节点来替换掉cur 都可以满足任务,这里采用第一种

注意:这种情况下,如果cur为root 时,同样可以用这种方法来完成删除

我们让rightMin = cur->right 然后一直找左

Node* rightMinParent = cur;
Node* rightMin = cur->_right;
while (rightMin->_left)//找到cur->_right的最左节点
{
	rightMinParent = rightMin;
	rightMin = rightMin->_left;
}

然后我们需要替换掉cur节点,并将rightMin的parent->left 设为nullptr 

cur->_key = rightMin->_key;//交换两个节点的值,让rightMin替换cur
//加一层判断
if (rightMin == rightMinParent->_left)
	rightMinParent->_left = rightMin->_right;
else
	rightMinParent->_right = rightMin->_right;

delete rightMin;
rightMin = nullptr;
return true;

 完成删除

遍历

key版本:

public:
    void InOrder()
    {
	    _InOrder(_root);
    	cout << endl;
    }
private:
    void _InOrder(Node* root)
    {
	    if (root == nullptr)
	    	return;

	    _InOrder(root->_left);
	    cout << root->_key << " ";
	    _InOrder(root->_right);
    }

key-val版本:

void _InOrder(Node* root)
{
	if (root == nullptr)
		return;

	_InOrder(root->_left);
	cout << root->_key << "-"<<root->_val<<"  ";
	_InOrder(root->_right);
}

利用中序遍历 对所有节点中的key (val)进行打印输出

  • 24
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,以下是一个 Python 实现二叉搜索树类,以及按照您的要求进行的测试代码: ```python class Node: def __init__(self, key): self.left = None self.right = None self.val = key class BSTree: def __init__(self): self.root = None # 插入节点 def insert(self, key): if self.root is None: self.root = Node(key) else: self._insert(key, self.root) def _insert(self, key, node): if key < node.val: if node.left is None: node.left = Node(key) else: self._insert(key, node.left) elif key > node.val: if node.right is None: node.right = Node(key) else: self._insert(key, node.right) else: return # 中序遍历 def inorder_traversal(self): if self.root is None: return [] else: return self._inorder_traversal(self.root) def _inorder_traversal(self, node): res = [] if node: res = self._inorder_traversal(node.left) res.append(node.val) res = res + self._inorder_traversal(node.right) return res # 查找节点 def search(self, key): if self.root is None: return None else: return self._search(key, self.root) def _search(self, key, node): if node is None or node.val == key: return node elif key < node.val: return self._search(key, node.left) else: return self._search(key, node.right) # 测试代码 bst = BSTree() keys = [4, 9, 5, 11, 25, 3, 12, 17, 1, 32, 20, 15] for key in keys: bst.insert(key) print(bst.inorder_traversal()) print(bst.search(17)) print(bst.search(38)) ``` 以上代码会输出以下内容: ``` [1, 3, 4, 5, 9, 11, 12, 15, 17, 20, 25, 32] <__main__.Node object at 0x7fe7f8d5b040> None ``` 其中,`[1, 3, 4, 5, 9, 11, 12, 15, 17, 20, 25, 32]` 是中序遍历的结果,`<__main__.Node object at 0x7fe7f8d5b040>` 是关键字为 17 的结点,`None` 是关键字为 38 的结点(不存在)。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值