C++进阶 —— 二叉树搜索树

目录

一,二叉搜索树定义

二,二叉搜索树操作

二叉搜索树的查找

二叉搜索树的插入

二叉搜索树的删除

二叉搜索树的实现

三,二叉搜索树的应用

K模型

KV模型

四,二叉搜索树的性能分析


平衡二叉搜索树:AVL树(高度平衡树)、红黑树;

一,二叉搜索树定义

        二叉搜索树,又称二叉排序树,是经典的数据结构,既具有链表快速插入和删除的特点,也有顺序表快速查找的特点;通过中序遍历可得到有序序列;

  • 或为一颗空树;
  • 或具有一下性质的二叉树;
    • 若其左子树不为空,则左子树所有节点的值都小于根节点的值;
    • 若其右子树不为空,则右子树所有节点的值都大于根节点的值;
    • 其左右子树也分别为二叉搜索树;
int arr[] = {5,3,4,1,7,8,2,6,0,9}

二,二叉搜索树操作

  • 查找,O(logN),最坏O(N);
  • 插入,O(logN);
  • 删除,O(logN);

二叉搜索树的查找

        从根节点开始,将目标值与当前节点比较,若等于当前节点,则直接返回;若比当前节点小则进入左子树进行比较,否则进入右子树进行比较。重复以上步骤直到找到目标值或遇到空节点停止查找。

if(root)
    if(root->data == x)
        return true;
    if(root->data > x)
        在其左子树查找
    if(root->data < x)
        在其右子树查找
else
     return false

二叉搜索树的插入

        从根节点开始,将新节点与当前节点比较大小,如果比当前节点小则进入左子树进行比较,否则进入右子树。当到达某个空节点时,将新节点插入到该位置。

  • 树为空,则直接插入,然后返回true;
  • 树不为空,按二叉搜索树性质查找插入位置,插入新节点;
插入新节点10:
1,按照二叉搜索树的性质,查找插入节点的位置
root-->5  5<10  root=root->right parent=root
root-->7  7<10  root=root->right parent=root
root-->8  8<10  root=root->right parent=root
root-->9  9<10  root=root->right parent=root

2,插入新节点

二叉搜索树的删除

  • 首先查找元素是否在二叉搜索树中,如不存在,则返回;
  • 否则,要删除的节点可能分为以下四种情况;
    • 要删除的节点无孩子,直接删除;
    • 要删除的节点只有左或右孩子,将其父节点直接指向左或右孩子;
    • 要删除的节点左右孩子节点都有,则在其左子树中找到最大的节点或右子树中找到最小的节点,用其值填补到被删除节点,然后在左子树或右子树中删除这个大的节点或最小节点;

二叉搜索树的实现

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()
	{
		while (_pRoot)
		{
			Erase(_pRoot->_data);
		}
	}

	pNode _Find(const T& data, pNode& pCur, pNode& pParent)
	{
		while (pCur)
		{
			if (data == pCur->_data)
				break;
			else if (data < pCur->_data)
			{
				pParent = pCur;
				pCur = pCur->_pleft;
			}
			else
			{
				pParent = pCur;
				pCur = pCur->_pright;
			}
		}
		return pCur;
	}
	pNode Find(const T& data)
	{
		pNode pParent = nullptr;
		pNode pCur = _pRoot;
		return _Find(key, pCur, pParent);
	}

	bool Insert(const T& data)
	{
		if (_pRoot == nullptr)
		{
			_pRoot = new Node(data);
			return true;
		}

		pNode pParent = nullptr;
		pNode pCur = _pRoot;
		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)
	{
		pNode pParent = nullptr;
		pNode pCur = _pRoot;
		pNode pdata = _Find(data, pCur, pParent);
		if (pdata == nullptr)
			return false;

		if (pCur->_pleft != nullptr && pCur->_pright != nullptr)
		{
			pParent = pCur;
			pCur = pCur->_pleft;
			while (pCur->_pright)
			{
				pParent = pCur;
				pCur = pCur->_pright;
			}
		}

		if (pParent != nullptr)
		{
			if (pCur->_pright == nullptr)
			{
				if (pCur->_data < pParent->_data)
					pParent->_pleft = pCur->_pleft;
				else
					pParent->_pright = pCur->_pleft;
			}
			else if (pCur->_pleft == nullptr)
			{
				if (pCur->_data < pParent->_data)
					pParent->_pleft = pCur->_pright;
				else
					pParent->_pright = pCur->_pright;
			}
			pdata->_data = pCur->_data;
		}
		else
		{
			if (pCur->_pright == nullptr)
				_pRoot = pCur->_pleft;
			else
				_pRoot = pCur->_pright;
		}

		delete pCur;
		pCur = nullptr;

		return true;
	}

	void _InOrder(const pNode pRoot)
	{
		pNode pCur = pRoot;
		if (pCur == nullptr)
			return;
		_InOrder(pCur->_pleft);
		cout << pCur->_data << " ";
		_InOrder(pCur->_pright);
	}
	void InOrder()
	{
		_InOrder(_pRoot);
	}

private:
	pNode _pRoot;
};

三,二叉搜索树的应用

K模型

  • 即只有key作为关键码(关键码即为待搜索的值),结构中只需存储key即可;
  • 如,一个单词word,判断该单词是否拼写正确;
    • 以单词集合中每个单词为key,构建一颗二叉搜索树;
    • 在二叉搜索树中检索该单词是否存在,存在即正确,不存在即错误;

KV模型

  • 每一个关键码key,都有与之对应的值Value,即<Key,Value>键值对;
  • 该种方式在生活中非常常见,如英汉字典就是中英文对应关系,通过英文可快速找到对应的中文,英文单词与对应中文<word、chinese>构造一种键值对;再比如统计单词次数,统计成功后,给定单词就可快速找到其出现次数,单词与出现次数<word,count>构成一种键值对;
template <class K, class V>
struct BSTNode
{
	BSTNode(const K& key = K(), const V& value = V())
		: _pleft(nullptr)
		, _pright(nullptr)
		, _key(key)
		, _value(value)
	{}

	BSTNode<K, V>* _pleft;
	BSTNode<K, V>* _pright;
	K _key;
	V _value;
};

template <class K, class V>
class BSTree
{
	typedef BSTNode<K, V> Node;
	typedef Node* pNode;
public:
	BSTree()
		:_pRoot(nullptr)
	{}
	~BSTree()
	{
		while (_pRoot)
		{
			Erase(_pRoot->_key);
		}
	}

	pNode _Find(const K& key, pNode& pCur, pNode& pParent)
	{
		while (pCur)
		{
			if (key == pCur->_key)
				break;
			else if (key < pCur->_key)
			{
				pParent = pCur;
				pCur = pCur->_pleft;
			}
			else
			{
				pParent = pCur;
				pCur = pCur->_pright;
			}
		}
		return pCur;
	}
	pNode Find(const K& key)
	{
		pNode pParent = nullptr;
		pNode pCur = _pRoot;
		return _Find(key, pCur, pParent);
	}

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

		pNode pParent = nullptr;
		pNode pCur = _pRoot;
		while (pCur)
		{
			pParent = pCur;
			if (key < pCur->_key)
				pCur = pCur->_pleft;
			else if (key > pCur->_key)
				pCur = pCur->_pright;
			else
				return false;
		}

		pCur = new Node(key, value);
		if (key < pParent->_key)
			pParent->_pleft = pCur;
		else
			pParent->_pright = pCur;

		return true;
	}

	bool Erase(const K& key)
	{
		pNode pParent = nullptr;
		pNode pCur = _pRoot;
		pNode pdata = _Find(key, pCur, pParent);
		if (pdata == nullptr)
			return false;

		if (pCur->_pleft != nullptr && pCur->_pright != nullptr)
		{
			pParent = pCur;
			pCur = pCur->_pleft;
			while (pCur->_pright)
			{
				pParent = pCur;
				pCur = pCur->_pright;
			}
		}

		if (pParent != nullptr)
		{
			if (pCur->_pright == nullptr)
			{
				if (pCur->_key < pParent->_key)
					pParent->_pleft = pCur->_pleft;
				else
					pParent->_pright = pCur->_pleft;
			}
			else if (pCur->_pleft == nullptr)
			{
				if (pCur->_key < pParent->_key)
					pParent->_pleft = pCur->_pright;
				else
					pParent->_pright = pCur->_pright;
			}
			pdata->_key = pCur->_key;
		}
		else
		{
			if (pCur->_pright == nullptr)
				_pRoot = pCur->_pleft;
			else
				_pRoot = pCur->_pright;
		}

		delete pCur;
		pCur = nullptr;

		return true;
	}

private:
	pNode _pRoot;
};

四,二叉搜索树的性能分析

  • 插入和删除操作都必须先查找,查找的效率代表了二叉搜索树中各个操作的性能;
  • 对有n个节点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是节点在二叉搜索树的深度的函数,即节点越深,则比较次数越多;
  • 但对同一个关键码集合,如各关键码插入的次序不同,可能得到不同结构的二叉搜索树;

最优情况下,二叉搜索树为完全二叉树O(logN),平均比较次数为logN;

最差情况下,二叉搜索树退化为单支树O(N),平均比较次数为N/2;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值