15.二叉树进阶(二叉搜索树的模拟实现和二叉搜索树的应用)

1.二叉搜索树

1.1 二叉搜索树概念

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

二叉搜索树:一颗二叉树,可以为空;如果不为空,满足以下性质:

1.若它的左子树不为空,则左子树上所有节点的值都小于根节点的值

2.若它的右子树不为空,则右子树上所有节点的值都大于根节点的值

3.它的左右子树也分别为二叉搜索树

  • 如下图所示:

2. 二叉搜索树的实现

2.1 节点的类模板

c 复制代码
// 节点的数据类型为K
template<class K>
struct BSTreeNode
{
    // 左子树根节点的地址
	BSTreeNode<K>* _left;
    // 右子树根节点的地址
	BSTreeNode<K>* _right;
	K _key;

    // 节点的构造函数
	BSTreeNode(const K& key)
		:_key(key)        // 节点存放的值为key
		,_left(nullptr)
		,_right(nullptr)
	{}
};

2.2 insert(插入的实现)

c 复制代码
bool Insert(const K& key)
	{
    	// 当树为空时
    	// 直接创建一个新节点来存放这个值
		if (_root == nullptr)
		{
			_root = new Node(key);
			return true;
		}

    	// 当树不为空时
    	// 将父节点的地址初始化为空,将cur节点的地址初始化为整棵树根节点的地址
		Node* parent = nullptr;
		Node* cur = _root;
    
    	// 当cur为空时,则说明找到了要插入的元素对应的位置
		while (cur)
		{
            // 如果要插入的值key大于当前节点的key,那么往右子树迭代
            // 如果要插入的值key小于当前节点的key,那么往左子树迭代
            // 如果插入的元素key与当前节点cur->_key的值相等,那么返回false(二叉搜索树不允许插入相同的元素)
			if (cur->_key < key)
			{
                // 对父节点和当前节点进行迭代
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
                // 对父节点和当前节点进行迭代
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

    	// 此时,parent指向cur,cur就是要插入元素的位置
    	// 所以我们new一个新的节点,将新节点的地址赋值给cur
		cur = new Node(key);
		if (parent->_key < key)
		{
            // 当我们插入的元素key大于parent->_key时,
            // parent->_right的位置就是cur的位置
			parent->_right = cur;
		}
		else
		{
            // 当我们插入的元素key小于parent->_key时,
            // parent->_left的位置就是cur的位置
			parent->_left = cur;
		}

		return true;
	}

2.3 InOrder(中序遍历)

为了可以访问根节点我们内置了一个中序遍历_InOrder,这样我们就可以访问类的私有成员变量_root,调用时直接调用InOrder就可以了

c 复制代码
void InOrder()
{
    _InOrder(_root);
    cout << endl;
}

private:
// 将内置的中序遍历封装成私有的成员函数,这样用户就不能够访问到了
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;

        // 中序遍历就是先访问左子树,打印根节点,在访问右子树
		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}

2.4 Find(查找key,找到返回值为真)

c 复制代码
bool Find(const K& key)
{
    // 将当前节点初始化为整颗二叉搜索树的根节点
	Node* cur = _root;
    
    // 如果当前节点为空,说明当前的二叉搜索树没有我们想找的节点
	while (cur)
	{
		if (cur->_key < key)
		{
            // 查找的元素key 大于 cur->_key,就往右子树移动
            
            // 迭代
			cur = cur->_right;
		}
		else if (cur->_key > key)
		{
            // 查找的元素key 小于 cur->_key,就往左子树移动
            
            // 迭代
			cur = cur->_left;
		}
		else
		{
            // 查找的元素key 等于 cur->_key,那么就是找到了
			return true;
		}
	}

    // 运行到此处,说明二叉搜索树中没有这个元素
	return false;
}

2.5 Erase(删除)

情况1和情况2:

情况3:

c 复制代码
bool Erase(const K& key)
{
    // 将父节点初始化为空,将cur节点初始化为整棵树的根节点
	Node* parent = nullptr;
	Node* cur = _root;
    
    
    // 想要删除元素key,那么首先要找到元素key
    // 寻找的方法和Find()函数的方法是一样的
	while (cur)
	{
		if (cur->_key < key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_key > key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
            // 代码运行到这里,说明我们已经找到了元素key,对应的节点为cur
            // 删除元素key,又分为三种情况:
			// 1、cur的左为空
			// 2、cur的右为空
			// 3、cur的左右都不为空,替换删除
            
			if (cur->_left == nullptr)
			{
                // 情况1:cur的左子树为空
                // 情况1又分为两个case
                
                // 情况1:case1:
                // 此处判断 parent == nullptr或者cur == _root 都是可以的
				// if (parent == nullptr)
				if (cur == _root)
				{
                    // case1: 当我们需要删除的节点cur为根节点_root时
                    // 此时,已知cur的左子树为空
                    // 因此cur->_right就是新的根节点
                    // 这样就链接完成了
					_root = cur->_right;
				}
				else
				{ 
                    // 情况1:case2:
					if (parent->_left == cur)
					{
                        // case2_1:当我们需要删除的cur是父节点的左子树的根节点
                        // 此时已知cur的左子树为空,
                        // 因此cur的右子树的根节点(cur->_right),需要代替cur节点连接到父节点的左子树上
						parent->_left = cur->_right;
					}
					else
					{
                        // case2_2:当我们需要删除的cur是父节点的右子树根节点
                        // 此时已知cur的左子树根节点为空,
                        // 因此cur的右子树的根节点(cur->_right),需要代替cur节点连接到父节点的右子树上
						parent->_right = cur->_right;
					}
				}

                // 链接完成之后,cur就可以被释放了
				delete cur;
			}
			else if (cur->_right == nullptr)
			{
                // 情况2:cur的右子树为空
                // 情况2由分为两个case
                // 具体的分析方法参考情况1
                
				//if (parent == nullptr)
				if (cur == _root)
				{
					_root = cur->_left;
				}
				else
				{
					if (parent->_left == cur)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
				}

				delete cur;
			}
			else
			{
                // 情况3:cur的左右子树都不为空
                // 此时我们需要使用替换删除
                // 替换也就是找cur左子树的最大节点 
                // 或者 右子树的最小节点来替换cur->_key 都可以
                
				// 我们采用找右子树的最小节点替换删除的方式
                // 1.找cur右子树,key值最小的节点
                
                // 将parent节点初始化为cur的地址,是为了防止cur->right就是key值最小节点
                // 注:如果cur->right就是最小节点,则minRight->_left为空,那么就无法进入循环
                // 那么如果将parent初始化为空,那么删除cur节点后,cur->right无法连接到cur的父节点
                // 因此parent初始化为空,且没有进入循环进行更新
				Node* parent = cur;
                
                // 2.将minRight初始化为cur右子树的根节点
				Node* minRight = cur->_right;
				while (minRight->_left)
				{
                    // 3.找cur右子树的key值最小节点,根据二叉搜索树的性质
                    // 我们只需要找根节点的左子树,直到minRight->left为空
                    // 此时minRight就是cur右子树的最小节点
					parent = minRight;
					minRight = minRight->_left;
				}

                // 将cur->_key 替换为最小节点的key值 minRight->_key
				cur->_key = minRight->_key;
                
                // 此时再干掉cur右子树key值最小节点就可以了
                // 代码运行到这里我们已经知道了minRight->left为空
                // (上述循环minRight->left迭代到空,才可以找到cur右子树的key值最小节点)
				if (minRight == parent->_left)
				{
                    // 如果minRight节点是parent的左子树
                    // 那么如果要干掉minRight,我们还需要将minRight遗留的右子树交给父节点parent的左子树
					parent->_left = minRight->_right;
				}
				else
				{
                    // 如果minRight是parent的右子树
                    // 那么如果要干掉minRight,我们还需要将minRight遗留的右子树交给父节点parent的右子树
					parent->_right = minRight->_right;
				}
                
                // 此时我们就可以干掉MinRight了
				delete minRight;
			}

			return true;
		}
	}

    // 代码运行到这里,说明cur为空,也没有找到我们要删除的节点
	return false;
}

2.6 FindR(使用递归的方式进行查找)

c 复制代码
	bool FindR(const K& key)
	{
		return _FindR(_root, key);
	}

private:
	// 私有成员函数
	bool _FindR(Node* root, const K& key)
	{
        // 当根节点迭代到空,说明当前整棵树已经遍历完了,那么返回flase
		if (root == nullptr)
			return false;
	
        // 如果root->_key < key,说明key所在的节点在root的右子树
		if (root->_key < key)
			return _FindR(root->_right, key);
        // 如果root->_key > key,说明key所在的节点在root的左子树
		else if (root->_key > key)
			return _FindR(root->_left, key);
		else
            // 此时,找到了key值对应的节点
			return true;
	}

2.7 insertR(使用递归的方式)

c 复制代码
// recursion n.递归
	bool InsertR(const K& key)
	{
		return _InsertR(_root, key);
	}

private:
	bool _InsertR(Node*& root, const K& key)  
	{
        // 3.最后迭代到root为空
		if (root == nullptr)
		{
            // 1.当root为空时
            // root如果是整棵树的根节点,root为空,那么直接将新节点的地址给root,这个新节点就是新的root节点
            // root如果是某个节点的地址
            // 假设root是pos位置节点的引用,pos的父节点是parent,pos是父节点的右子树的根节点,
            // 那么直接将新节点的地址给root,这个新节点父节点的右子树的根节点
			root = new Node(key);
			return true;
		}

        
        // 2.当root不为空时
        // 如果root->_key < key,说明key要插入的位置在root的右子树
		if (root->_key < key)
			return _InsertR(root->_right, key);
        // 如果root->_key > key,说明key要插入的位置在root的左子树
		else if (root->_key > key)
			return _InsertR(root->_left, key);
		else
            // 运行到这里说明key值与搜索二叉树中的值重复了,二叉搜索树不允许插入重复的值,因此返回false
			return false;
	}

2.8 EraseR(使用递归的方式进行删除)

c 复制代码
	bool EraseR(const K& key)
	{
		return _EraseR(_root, key);
	}

private:
	bool _EraseR(Node*& root, const K& key)
	{
        // 当迭代到root为空,说明没有找到我们需要删除的key值,所以返回false
		if (root == nullptr)
		{
			return false;
		}

        // 如果root->_key < key,说明删除的key的位置在root的右子树
		if (root->_key < key)
		{
			return _EraseR(root->_right, key);
		}
        // 如果root->_key > key,说明删除的key的位置在root的左子树
		else if (root->_key > key)
		{
			return _EraseR(root->_left, key);
		}
		else
		{
            // 代码运行到这里说明我们已经定位到了我们要删除的节点了
            // 此时root就是我们要删除的节点
            // 在删除root节点之前,需要将root的父亲节点,与root的后续节点进行连接
            // 将root交给del,将root后面的节点连接之后
            // 就可以删掉del节点了
			Node* del = root;
            
            
            
            // 情况1:当root的右子树为空时
			if (root->_right == nullptr)
			{
                // 此时root为要删除的节点,已知右子树为空
                // 所以用root的左子树的根节点覆盖root
                // 因为使用的是传引用的方式,修改root的地址,就是修改root父亲节点的指向
				root = root->_left;
			}
			else if (root->_left == nullptr)
			{
                // 情况2:
                // 要删除节点的左子树为空
				root = root->_right;
			}
			else
			{
                // 情况3:要删除节点的左右子树都不空
                // 我们需要采用替换删除的方法
                // 此时root就是我们需要删除的节点
                // 第一步:我们先去找需要删除节点的右子树的最小节点
				Node* minRight = root->_right;
                
                // 根据搜索二叉树的性质,在根节点的左子树找二叉树key值最小的节点
                // 当minRight->_left为空时,迭代结束,并且找到了key最小的minRight节点
				while (minRight->_left)
				{
					minRight = minRight->_left;
				}

                //此时,已经找到了右子树的最小节点,将它的key元素与root节点的key值进行交换
				swap(root->_key, minRight->_key);

                // 将root节点的key值与minRight的key值进行交换之后,
                // 那么需要删除的节点就是minRight(minRight是右子树的最小节点)
                // 迭代,直到满足情况1或者情况2,迭代结束
				return _EraseR(root->_right, key);
			}

            // 对于情况1和情况2,是释放del节点
			delete del;
			return true;
		}
	}

2.9 构造函数

c 复制代码
BSTree()
	:_root(nullptr)
{}

2.10 析构函数

c 复制代码
~BSTree()
{
    // 销毁_root节点
	Destroy(_root);
    // 将根节点置空,防止野指针
	_root = nullptr;
}

private:
	void Destroy(Node* root)
	{
		if (root == nullptr)
			return;

        // 递归销毁根节点的左子树和右子树
		Destroy(root->_left);
		Destroy(root->_right);
        
        // 销毁当前节点
		delete root;
	}

2.11 拷贝构造

c 复制代码
	BSTree(const BSTree<K>& t)
	{
		_root = Copy(t._root);
	}

private:
	Node* Copy(Node* root)
	{
        // 当root为空,则不会继续向下迭代拷贝
		if (root == nullptr)
			return nullptr;

        // 创建一个根节点(用根节点的key值进行初始化),给新二叉树的根节点
		Node* newRoot = new Node(root->_key);
        // 向左子树进行迭代,返回左子树根节点的地址,给到newRoot->_left
		newRoot->_left = Copy(root->_left);
        // 向右子树进行迭代,返回右子树根节点的地址,给到newRoot->_right
		newRoot->_right = Copy(root->_right);

        // 返回新的二叉树根节点的地址
		return newRoot;
	}

2.12 赋值重载

c 复制代码
BSTree<K>& operator=(BSTree<K> t)
{
    // 交换根节点的地址
    // 注:t对象的二叉树只是一个临时对象(不是传引用对象)
	swap(_root, t._root);
    
    // 返回二叉树对象
	return *this;
}

二叉搜索树的类模板的完整实现

c 复制代码
namespace qwy
{
    // 二叉树节点的类模板
	template<class K>
	struct BSTreeNode
	{
		BSTreeNode<K>* _left;
		BSTreeNode<K>* _right;
		K _key;

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

    // 二叉搜索树的类模板
	template<class K>
	class BSTree
	{
        // 将二叉类模板类型定义为 Node
		typedef BSTreeNode<K> Node;
	public:
        // 构造函数
		BSTree()
			:_root(nullptr)
		{}

		// 拷贝构造函数
		BSTree(const BSTree<K>& t)
		{
			_root = Copy(t._root);
		}

        // 运算符重载函数
        BSTree<K>& operator=(BSTree<K> t)
        {
            swap(_root, t._root);
            return *this;
        }

        // 析构函数
		~BSTree()
		{
			Destroy(_root);
			_root = nullptr;
		}

        // 插入函数
		bool Insert(const K& 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->_right = cur;
			}
			else
			{
				parent->_left = cur;
			}

			return true;
		}

        // 查找函数
		bool Find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					cur = cur->_left;
				}
				else
				{
					return true;
				}
			}

			return false;
		}

        
        // 删除函数
		bool Erase(const K& 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
				{
					// 1、左为空
					// 2、右为空
					// 3、左右都不为空,替换删除
					if (cur->_left == nullptr)
					{
						//if (parent == nullptr)
						if (cur == _root)
						{
							_root = cur->_right;
						}
						else
						{
							if (parent->_left == cur)
							{
								parent->_left = cur->_right;
							}
							else
							{
								parent->_right = cur->_right;
							}
						}

						delete cur;
					}
					else if (cur->_right == nullptr)
					{
						//if (parent == nullptr)
						if (cur == _root)
						{
							_root = cur->_left;
						}
						else
						{
							if (parent->_left == cur)
							{
								parent->_left = cur->_left;
							}
							else
							{
								parent->_right = cur->_left;
							}
						}

						delete cur;
					}
					else
					{
						// 右子树的最小节点
						Node* parent = cur;
						Node* minRight = cur->_right;
						while (minRight->_left)
						{
							parent = minRight;
							minRight = minRight->_left;
						}

						cur->_key = minRight->_key;
						if (minRight == parent->_left)
						{
							parent->_left = minRight->_right;
						}
						else
						{
							parent->_right = minRight->_right;
						}
						delete minRight;
					}

					return true;
				}
			}

			return false;
		}

        // 中序遍历
		void InOrder()
		{
			_InOrder(_root);
			cout << endl;
		}

        // 插入(使用递归的方式)
		bool InsertR(const K& key)
		{
			return _InsertR(_root, key);
		}

        // 查找(使用递归的方式)
		bool FindR(const K& key)
		{
			return _FindR(_root, key);
		}

        // 删除(使用递归的方式)
		bool EraseR(const K& key)
		{
			return _EraseR(_root, key);
		}

	private:
        // 析构函数内部调用这个函数
		void Destroy(Node* root)
		{
			if (root == nullptr)
				return;

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

        // 拷贝构造函数内部调用这个函数
        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;
        }

        // Erase内部调用这个函数
		bool _EraseR(Node*& root, const K& key)
		{
			if (root == nullptr)
			{
				return false;
			}

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

					swap(root->_key, minRight->_key);

					// 转换成在子树中去删除节点
					return _EraseR(root->_right, key);
				}

				delete del;

				return true;
			}
		}

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

			if (root->_key < key)
				return _InsertR(root->_right, key);
			else if (root->_key > key)
				return _InsertR(root->_left, key);
			else
				return false;
		}

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

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

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

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

	private:
		Node* _root = nullptr;
	};
}

3.测试我们实现的二叉搜索树

3.1 测试中序遍历

c 复制代码
void TestBSTree1()
{
	int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
	BSTree<int> t;
	for (auto e : a)
	{
		t.Insert(e);
	}

	t.InOrder();
}

4.二叉搜索树的应用

4.1 KV模型

c 复制代码
// 根据二叉搜索树改造的KV模型
namespace KV
{
    // 二叉树节点的类模板
	template<class K, class V>
	struct BSTreeNode
	{
        // 指向左子树
		BSTreeNode<K, V>* _left;
        // 指向右子树
		BSTreeNode<K, V>* _right;
		K _key;
		V _value;

        // 构造函数
        // 需要传入key值,和value值
		BSTreeNode(const K& key, const V& value)
			:_key(key)
			,_value(value)
			, _left(nullptr)
			, _right(nullptr)
		{}
	};

    // 二叉树的类模板
	template<class K, class V>
	class BSTree
	{
		typedef BSTreeNode<K, V> Node;
	public:
        // 插入
		bool Insert(const K& key, const V& value)
		{
			if (_root == nullptr)
			{
				_root = new Node(key, value);
				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就是新节点要插入的位置
			cur = new Node(key, value);
			if (parent->_key < key)
			{
				parent->_right = cur;
			}
			else
			{
				parent->_left = cur;
			}

			return true;
		}

        // 查找
		Node* Find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					cur = cur->_right;
				}
				else if (cur->_key > key)
				{
					cur = cur->_left;
				}
				else
				{
					return cur;
				}
			}

			return nullptr;
		}

        // 中序遍历
		void Inorder()
		{
			_Inorder(_root);
		}

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

			_Inorder(root->_left);
			cout << root->_key << ":" << root->_value << endl;
			_Inorder(root->_right);
		}
	private:
		Node* _root = nullptr;
	};
}

应用场景一:

c 复制代码
/*
	英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对;
*/

void TestBSTree2()
{
    // 创建一个二叉树对象
	KV::BSTree<string, string> dict;
    
    // 插入KV值
	dict.Insert("sort", "排序");
	dict.Insert("string", "字符串");
	dict.Insert("left", "左边");
	dict.Insert("right", "右边");

	string str;
	while (cin>>str)
	{
        // 进行查找
        // Node* Find(const K& key),返回key所在节点的地址,如果没有找到,则返回空
		KV::BSTreeNode<string, string>* ret = dict.Find(str);
		if (ret)
		{
			cout << ret->_value << endl;
		}
		else
		{
			cout << "无此单词" << endl;
		}
	}
}

应用情景二:

c 复制代码
/*
	如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对。
*/

void TestBSTree3()
{
	// 统计水果出现的次数
	string arr[] = { "苹果", "西瓜", "香蕉", "草莓","苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };

    // 创建一个二叉树对象
	KV::BSTree<string, int> countTree;
	for (auto e : arr)
	{
 		 // 先查找水果在不在搜索树中
		 // 1、不在,说明水果第一次出现,则插入<水果, 1>
		 // 2、在,则查找到的节点中水果对应的次数++
		auto* ret = countTree.Find(e);
		if (ret == nullptr)
		{
			countTree.Insert(e, 1);
		}
		else
		{
			ret->_value++;
		}
	}

	countTree.Inorder();
}

5. 二叉搜索树的性能分析

  • 插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能:
  • 对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
  • 但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:
  • 最优情况下:二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为:log_2 N
  • 最差情况下:二叉搜索树退化为单支树(或者类似单支),其平均比较次数为:frac{N}{2}
  • 问题:如果退化成单支树,二叉搜索树的性能就失去了。那能否进行改进,不论按照什么次序插入关键码,二叉搜索树的性能都能达到最优?后续章节的AVL树和红黑树再来解释
相关推荐
好奇龙猫1 分钟前
【学习AI-相关路程-mnist手写数字分类-win-硬件:windows-自我学习AI-实验步骤-全连接神经网络(BPnetwork)-操作流程(3) 】
人工智能·算法
霁月风36 分钟前
设计模式——适配器模式
c++·适配器模式
sp_fyf_202437 分钟前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-01
人工智能·深度学习·神经网络·算法·机器学习·语言模型·数据挖掘
ChoSeitaku1 小时前
链表交集相关算法题|AB链表公共元素生成链表C|AB链表交集存放于A|连续子序列|相交链表求交点位置(C)
数据结构·考研·链表
偷心编程1 小时前
双向链表专题
数据结构
香菜大丸1 小时前
链表的归并排序
数据结构·算法·链表
jrrz08281 小时前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表
oliveira-time1 小时前
golang学习2
算法
咖啡里的茶i1 小时前
Vehicle友元Date多态Sedan和Truck
c++
海绵波波1071 小时前
Webserver(4.9)本地套接字的通信
c++