实现二叉搜索树

目录

二叉搜索树

1.概念

 2.实现

①框架

②Insert

③Find

④Erase

⑤~BStree

⑥BStree(const BStree& t)

⑦FindRecurrence(Find的递归写法)

⑧InsertRecurrence(Insert的递归写法)

⑨EraseRecurrence(Erase的递归)


二叉搜索树

1.概念

                       二叉搜索树又称二叉排序树,它或者是一个空树,或者具有以下性质

左子树任意一个节点的值都小于根节点的值

右子树任意一个节点的值都大于根节点的值

它的左右子树也是二叉搜索树

        那它的时间复杂度是多少?我们查找7从上图来看,只用4次,看样子只用查找高度次,时间复杂度是不是O(lgn)呢?那是理想情况,那如果这个二叉搜索树是这样呢?

         那时间复杂度就为n/2了,所以二叉搜索树的时间复杂度是O(N)。

 2.实现

我们先来实现一下:

①大致架子:

template <class K>
struct BSTreeNode
{
	BSTreeNode<K>* _left;
	BSTreeNode<K>* _right;

	K _key;
};

template<class K>
class BStree
{
	typedef BSTreeNode<K> Node;
private:
	Node* _root= nullptr;
};

 ②Insert

先来写一个插入:

        插入还是挺简单的,定义两个指针,一个向下走找插入的位置,一个紧跟其后,作为要插入结点的父亲,最后将他们连接起来就好。

bool Insert(const K& key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);
		}
		Node* cur = _root;
		Node* prev = _root;
		while (cur)
		{
			prev = cur;
			if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else if (cur->_key < key)
			{
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}
		if (prev->_key > key)
		{
			prev->_left = new Node(key);
		}
		if (prev->_key < key)
		{
			prev->_right = new Node(key);
		
			return true;
	}

 写一个中序遍历,然后插入一段数据,验证下我们的insert写的对不对。

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

插入:

结果:

 ③Find

与插入大同小异

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

 ④Erase

重头戏

情况一:删除2

        这种删除最让人省心了,让父节点指向它的左右子树的NULL节点就行,然后释放它。

情况二:删除8

        巧了,父亲走掉了,并且它没有兄弟,像极了linux中的孤儿进程,我们可以让8的父节点连接它剩下的一个节点。

情况三:删除3

        完了,删除3之后留下3个孤儿,怎么办?3的父节点也只能接受一个。是时候抓个节点来当父亲了,小小年纪但此重任,谁比较好呢?

        要么选择被删除节点的左子树最大值节点or右子树最小值节点,和被删除值替换,既不破坏树的整体结构也能,很好的删除节点。 

        巧妙的是左子树最大值节点or右子树最小值节点,要么只有一个子树,要么没有子树,这被删除值和所选位置两个值替换之后,就又转回情况一和情况二了。一会可以巧妙地运用一下。

 情况一二的实现:

				//1. 被删除节点只有一个子节点,或者没子节点
				if (cur->_left == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_right;
					}
					else
					{
						if (prev->_left == cur)
						{
							prev->_left = cur->_right;
						}
						else if (prev->_right == cur)
						{
							prev->_right = cur->_right;
						}
					}
					delete cur;
				}
				else if (cur->_right == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_left;
					}
					else
					{
						if (prev->_left == cur)
						{
							prev->_left = cur->_left;
						}
						else if (prev->_right == cur)
						{
							prev->_right = cur->_left;
						}
					}
					delete cur;
				}

情况三:

                else//2.左右都有孩子,替换法
				{
					Node* parentminRt = cur;
					Node* minRight = cur->_right;
					while (minRight->_left)
					{
						parentminRt = minRight;
						minRight = minRight->_left;
					}
					cur->_key = minRight->_key;

					if (parentminRt->_left == nullptr)
					{
						parentminRt->_left = minRight->_right;
					}
					else
					{
						parentminRt->_right = minRight->_right;
					}

					delete minRight;
				}

⑤~BStree

        来个小菜,去瓦解这颗树,调用递归是比较实用的方法。因为是要从根去瓦解树,这样在一个函数中递归就比较麻烦,我们可以在析构函数中调用另一个函数去递归。上面中序遍历用到的方法一样。

具体实现:

private:
	void Destory(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		Destory(root->_left);
		Destory(root->_right);
		delete root;
	}
public:
	~BStree()
	{
		Destory(_root);
		_root = nullptr;
	}

⑥BStree(const BStree<K>& t)

        使用现代方法(通过遍历然后调用Insert函数)这样是不行的,因为有可能导致树的结构和原树截然不同。所以还是递归好用,跟析构函数差不多。

	Node* Construct(Node* root)
	{
		if (root == nullptr)
			return nullptr;
		Node* ntree = new Node(root->_key);
		ntree->_left = Construct(root->_left);
		ntree->_right = Construct(root->_right);
		return ntree;
	}
    BStree(const BStree<K>& bt)
	{
		_root = Construct(bt._root);
	}

结果:

        当我们写出拷贝构造函数之后,编译器不会再生成默认构造函数,因为一旦我们实现了构造函数或者拷贝构造函数,编译器就不会再生成默认构造函数。我们可以这样写,让编译器强制生成构造函数。这是c++11中的语法,我后面会写。

	BStree() = default;

⑦FindRecurrence(Find的递归写法)

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

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

public:

	bool FindRecurrence( const K& key)
	{
		return _FindRecurrence(_root, key);
	}

⑧InsertRecurrence(Insert的递归写法)

        注意关键是第一个参数是引用传参,点睛之笔!

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

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

public:

	bool InsertRecurrence(const K& key)
	{
		return _InsertRecurrence(_root, key);
	}

我们来验证下:

 监视窗口:

 

⑨EraseRecurrence(Erase的递归)

最后用分治思想,再次分为小的子树。

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

		if (root->_key > key)
		{
			_EraseRecurrence(root->_left, key);
		}
		else if (root->_key < key)
		{
			_EraseRecurrence(root->_right, key);
		}
		else
		{
			Node* del = root;
			if (root->_left == nullptr)
			{
				root = root->_right;
			}
			else if (root->_right == nullptr)
			{
				root = root->_left;
			}
			else
			{
				Node* minRight = root->_right;
				while (minRight->_left)
				{
					minRight = minRight->_left;
				}
				swap(root->_key, minRight->_key);
				return _EraseRecurrence(root->_right,key);
			}
			return true;
		}
	}

public:
	bool EraseRecurrence(const K& key)
	{
		return _EraseRecurrence(_root, key);
	}

删除只有一个子节点的节点。

 删除两个节点

 

 

                                                                 That's all.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值