二叉搜索树

二叉搜索树

二叉搜索树是一个高效的数据结构,查找、插入、删除的平均时间复杂度都是 O(log2n)

对应树中的一个节点 u 满足:

  • 如果 u 存在左子树,那么左子树中所有点的 key 都小于(大于) u 的 key
  • 如果 u 存在右子树,那么右子树中所有点的 key 都大于(小于) u 的 key

二叉搜索树的缺点:

​ 如果按照优先级(从大到小、从小到大)的顺序插入元素,那么二叉搜索树退化为一个链表。查找、插入、删除的时间复杂度退化为 O(n)。后面的平衡树、红黑树等都是为了解决二叉搜索树的这个缺点。

  • treeNode 的实现:
template <class T>
struct treeNode
{
	T value;
	treeNode* left, * right; // 标准的二叉树

	treeNode() { left = right = nullptr; }
	treeNode(const T& theValue) : value(theValue) { left = right = nullptr; }
	treeNode(const T& theValue, treeNode* theLeft, treeNode* theRight) : value(theValue)
	{
		left = theLeft;
		right = theRight;
	}
};
  • binarySearchTree 的实现:
// less 满足节点的左子树中所有的点都小于根,右子树都大于根,greater 相反
template <class K, class E, class Compare=less<K>>
class binarySearchTree
{
public:
	binarySearchTree()
	{
		root = nullptr;
		_size = 0;
	}
	explicit binarySearchTree(const Compare& x) : cmp(x) { binarySearchTree(); }
	~binarySearchTree(); // 内存释放

	int size() const { return _size; }
	pair<const K, E>* find(const K& theKey);
	treeNode<pair<const K, E>>* insert(const pair<const K, E>& thePair);
	bool erase(const K& theKey);
        // 按照优先级打印全部元素
        void display() const { display(root); }
	void display(const treeNode<pair<const K, E>>* node)
	{
		if (node == nullptr) return;
		display(node->left);
		cout << "{" << node->value.fist << ":" << node->value.second << "}" << endl;
		display(node->right);
	}

private:
	treeNode<pair<const K, E>>* root;
	int _size;
	Compare cmp;
};
  • ~binaryTreeNode 的实现 (偷懒了) :时间复杂度 O(n),空间复杂度 O(n)
template<class K, class E, class Compare>
binarySearchTree<K, E, Compare>::~binarySearchTree()
{
    // 懒得写 dfs
	stack<treeNode<pair<const K, E>>*> stk;
	if (root) stk.push(root);
	while (!stk.empty())
	{
		auto t = stk.top();
		stk.pop();
		if (t->left) stk.push(t->left);
		if (t->right) stk.push(t->right);
		delete t;
	}
}
  • find 的实现:平均时间复杂度 O(log2n),最坏时间复杂度 O(n)
template<class K, class E, class Compare>
pair<const K, E>* binarySearchTree<K, E, Compare>::find(const K& theKey)
{
	auto cur = root;
	while (cur != nullptr)
	{
		if (cur->value.first == theKey) return &(cur->value);
		else if (cmp(cur->value.first, theKey)) cur = cur->right;
		else cur = cur->left;
	}
	return nullptr;
}
  • insert 的实现:平均时间复杂度 O(log2n),最坏时间复杂度 O(n)
    • 如果存在 key,则修改其元素的值
    • 否则,在空节点处插入新节点
template<class K, class E,  class Compare>
treeNode<pair<const K, E>>* binarySearchTree<K, E, Compare>::insert(const pair<const K, E>& thePair)
{
    // pre 记录 cur 的父节点
	treeNode<pair<const K, E>>* cur = root, *pre = nullptr;
	while (cur)
	{
		pre = cur;
		if (cur->value.first == thePair.first) // 如果 key 存在,则修改其 value
		{
			cur->value.second = thePair.second;
			return cur;
		}
		else if (cmp(cur->value.first, thePair.first)) cur = cur->right;
		else cur = cur->left;
	}
	
    // key 不存在时,pre 是最后一个节点,在 pre 后插入
	auto newNode = new treeNode<pair<const K, E>>(thePair);
	if (root != nullptr)
	{
		if (cmp(pre->value.first, thePair.first)) pre->right = newNode;
		else pre->left = newNode;
	}
	else root = newNode;
	_size++;
	return newNode;
}
  • erase 的实现:平均时间复杂度 O(log2n),最坏时间复杂度 O(n)

    • 如果节点 u 不存在,插入失败
    • 删除根节点的情况需要特殊处理
    • 节点 u 存在:
      • u 的两个子树都存在:在左子树中寻找最大点 son,将 son 放置到 u 处,然后按照只存在一颗或者不存在子树的情况删除 son 为根的子树。
      • u 只有一个或者没有子树:类似于链表删除元素即可

template<class K, class E, class Compare>
bool binarySearchTree<K, E, Compare>::erase(const K& theKey)
{
	treeNode<pair<const K, E>>* cur = root, *pre = nullptr;
	while (cur)
	{
		if (cur->value.first == theKey) break;
		else
		{
			pre = cur;
			if (cmp(cur->value.first, theKey)) cur = cur->right;
			else cur = cur->left;
		}
	}
    // 不存在该 key 则删除失败
	if (cur == nullptr) return false;

    // 讨论删除点的两个子树都存在的情况
	if (cur->left && cur->right)
	{
		treeNode<pair<const K, E>>* son = cur->left, *parent = cur;
		// 寻找左子树中最大的值,也就是沿着最右路径寻找
        // 也可以寻找右子树中最小的值,沿着右子树的最左路径即可
		while (son->right != nullptr)
		{
			parent = son;
			son = son->right;
		}

		auto newNode = new treeNode<pair<const K, E>>(son->value, cur->left, cur->right);
		// 删除的是根节点直接替换根节点即可
		if (pre == nullptr) root = newNode;
		else if (cur == pre->left) pre->left = newNode;
		else pre->right = newNode;

		// 如果左子树不含右子树,那么左子树的最大值为其根节点
		if (parent == cur)
			pre = newNode;
		else pre = parent;

		delete cur;
		// 下面删除左子树中的最大节点
		cur = son;
	}

	// 只有一个子树或者没有子树的情况
	treeNode<pair<const K, E>>* node = nullptr;
	if (cur->left)
		node = cur->left;
	else
		node = cur->right;

	if (cur == root)
		root = node;
	else
	{
		if (cur == pre->left)
			pre->left = node;
		else
			pre->right = node;
	}

	_size--;
	delete cur;
	return true;
}
posted @   PigPigHero  阅读(70)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示