二叉搜索树

目录

基础知识

一、性质:(非空树时)

二、基本操作:

三、性能:

四、应用:

代码实现


基础知识

 

一、性质:(非空树时)

  •     若它的左子树不为空,则左子树上所有节点的值都比根小
  •     若它的右子树不为空,则右子树上所有节点的值都比根大
  •     它的左右子树也分别为二叉搜索树

 

二、基本操作:

1、查找:

  • 若根节点不为空:
  • ​ 如果根节点key == 查找key 返回true
  • ​ 如果根节点key > 查找key 在其左子树进行查找
  • ​ 如果根节点key < 查找key 在其右子树进行查找

2、插入:

  • 树为空,则直接插入
  • 树不空,按二叉搜索树性质查找插入位置,插入新节点

3、删除:

 分为三种情况:

  • 情况​a:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点
  • 情况b:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点
  • 情况c:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中, 再来处理该结点的删除问题

 

三、性能:

  • 最优情况:二叉搜索树为完全二叉树(近似折半查找),查找效率:O(LogN)
  • 最差情况:二叉搜索树为单支树(近似顺序查找),查找效率:O(N)

 

四、应用:

  •  判断一个单词是否拼写正确
  •  模拟实现一个简单的字典
  •  log文件中每个异常重复的IP地址各出现多少次

代码实现

template<class T>
struct BSTNode
{
	BSTNode(const T& data = T())
		:_pLeft(nullptr)
		, _pRight(nullptr)
		, _data(data)
	{ }

	BSTNode<T>* _pLeft;
	BSTNode<T>* _pRight;
	T _data;
};

template<class T>
class BSTree
{
	typedef BSTNode<T> Node;
	typedef Node* pNode;
public:
	BSTree()
		:_pRoot(nullptr)
	{}
	~BSTree()
	{
		Destroy(_pRoot);
	}
	pNode Find(const T& data)   //查找
	{
		pNode pCur = _pRoot;
		while (pCur)
		{
			if (data == pCur->_data)
				return pCur;
			else if (data < pCur->_data)
				pCur = pCur->_pLeft;
			else
				pCur = pCur->_pRight;
		}
		return nullptr;
	}
	bool Insert(const T& data)   //插入
	{
		if (_pRoot == nullptr) //树为空
		{
			_pRoot = new Node(data);
			return true;
		}
		pNode pCur = _pRoot;
		pNode pParent = nullptr;
		while (pCur)
		{
			pParent = pCur;
			if (data < pCur->_data)
				pCur = pCur->_pLeft;
			else if (data > pCur->_data)
				pCur = pCur->_pRight;
			else
				return false;  //元素已存在
		}
		pCur = new Node(data);
		if (data < pParent->_data)
			pParent->_pLeft = pCur;
		else
			pParent->_pRight = pCur;
		return true;

	}
	bool Erase(const T& data)   //删除
	{
		if (nullptr == _pRoot)
			return false;
		pNode pCur = _pRoot;
		pNode pParent = nullptr;
		while (pCur)
		{
			if (pCur->_data == data)
				break;
			else if (pCur->_data > data)
			{
				pParent = pCur;
				pCur = pCur->_pLeft;
			}
			else
			{
				pParent = pCur;
				pCur = pCur->_pRight;
			}
		}
		if (nullptr == pCur)    //没有该数据
			return false;
		if (nullptr == pCur->_pRight)  //当前节点只有左孩子或左孩子为空,可直接删除
		{
			if (pCur == _pRoot)    //删除根节点
				_pRoot = pCur->_pLeft;
			else if (pCur == pParent->_pLeft)
				pParent->_pLeft = pCur->_pLeft;
			else
				pParent->_pRight = pCur->_pLeft;

		}
		else if (nullptr == pCur->_pLeft)  //当前节点只有右孩子,可直接删除
		{
			if (pCur == _pRoot)   //删除根节点
				_pRoot = pCur->_pRight;
			else if (pCur == pParent->_pLeft)
				pParent->_pLeft = pCur->_pRight;
			else
				pParent->_pRight = pCur->_pRight;
		}
		else  //当前节点左右孩子都存在,不好直接删除,需要在其子树中找一个替代节点
		{
			/* 寻找替代节点:二选一
			1、找其左子树中的最大节点,即左子树中最右侧的节点,
			2、找其右子树中最小的节点,即右子树中最小的节点
			替代节点找到后,将替代节点中的值交给待删除节点,转换成删除替代节点
			*/
			pNode del = pCur->_pRight;  //在待删节点的右子树中找最小替代节点
			pParent = pCur;
			while (del->_pLeft)    //右子树的最小节点在左边
			{
				pParent = del;
				del = del->_pLeft;
			}
			pCur->_data = del->_data;   //替换数据
			if (pParent->_pLeft == del)
				pParent->_pLeft = del->_pRight;
			else
				pParent->_pRight = del->_pRight;
			pCur = del;
		}

		delete pCur;
		pCur = nullptr;

		return true;
	}
	void INOrder()   //中序遍历
	{
		InOrder(_pRoot);
	}
private:
	void InOrder(pNode pRoot)   //中序遍历
	{
		if (pRoot)
		{
			InOrder(pRoot->_pLeft);
			cout << pRoot->_data << " ";
			InOrder(pRoot->_pRight);
		}
	}
	void Destroy(pNode& pRoot)   //销毁
	{
		if (pRoot)
		{
			Destroy(pRoot->_pLeft);
			Destroy(pRoot->_pRight);
			pRoot = nullptr;
		}
	}
private:
	pNode _pRoot;
};



--------------------测试代码---------------------
void testptree()
{
	BSTree<int> b;
	b.Insert(5);
	b.Insert(3);
	b.Insert(4);
	b.Insert(1);
	b.Insert(2);
	b.Insert(7);
	b.Insert(6);
	b.Insert(9);
	b.Insert(8);
	cout << "插入后的中序遍历:";
	b.INOrder();
	cout << endl;
	BSTNode<int>* s = b.Find(5);
	cout << s->_data<< endl;
	b.Erase(7);
	b.Erase(2);
	b.Erase(8);
	cout << "删除后的中序遍历:";
	b.INOrder();

}
int main()
{
	testptree();
	system("pause");
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值