C++: 二分探索木 (不均衡)

ここに画像の説明を挿入

1. 二分探索木 (key_value モデル)

  • ツリーのノード定義:
	template<class K, class V>
	struct BSTreeNode
	{
    
    
		BSTreeNode<K, V>* _left;
		BSTreeNode<K, V>* _right;
		//key是用于实现搜索功能以及搜索树其他功能的关键值
		K _key;
		//value用于存储数据
		V _value;
		//树结点的默认构造函数
		BSTreeNode(const K& key = K(), const V& value = V())
			:_left(nullptr)
			, _right(nullptr)
			, _key(key)
			, _value(value)
		{
    
    }
	};
  • バイナリ ツリーがバイナリ検索ツリーの場合、次のプロパティがあります。
    • 左側のサブツリー内のすべてのノードのキー値がルート ノードのキー値よりも高いキー値小さい
    • 右のサブツリー内のすべてのノードのキー値がルート ノードのキー値よりも高いキー値大きい
    • 二分探索ツリーの左側と右側のサブツリーは依然として二分探索ツリーです
      ここに画像の説明を挿入
  • 二分探索木再帰的定義を満たす
  • 証明するのは簡単ですが、二分探索木の順序走査シーケンスです昇順(つまり、二分探索木にはソート機能があり、二分ソート木とも呼ばれます)
  • 検索ツリーの定義に従って、ツリー内で同じ値のキーはありませんノードがあるため、検索ツリーにも重複排除機能
  • 二分探索ツリー C++ クラス テンプレートの概要:
	template<class K, class V>
	class BSTree
	{
    
    
		typedef BSTreeNode<K, V> Node;
	public:
	//供外界调用的功能接口(通过复用内部私有接口实现)
		
		//强制生成类的默认构造函数
		BSTree() = default; 
		//利用前序遍历递归完成拷贝构造
		BSTree(const BSTree<K,V>& t)
		{
    
    
			_root = _Copy(t._root);
		}
		//通过复用拷贝构造实现赋值运算符重载
		BSTree<K,V>& operator =(BSTree<K,V>t)
		{
    
    
			std::swap(t._root, _root);
			return (*this);
		}
		//插入节点
		bool InsertR(const K& key, const V& value)
		{
    
    
			return _InsertR(_root,key, value);
		}
		//查找节点
		Node* Find(const K& key)
		{
    
    
			return _FindR(_root,key);
		}
		//删除节点(这里调用递归的接口)
		bool Erase(const K& key)
		{
    
    
			return _EraseR(_root, key);
		}
		//中序遍历搜索树
		void InOrder()
		{
    
    
			_InOrder(_root);
		}
		//搜索树的析构函数
		~BSTree()
		{
    
    
			_Destroy(_root);
		}
	protected:
	//私有功能接口
		//用于实现拷贝构造的辅助函数(前序递归实现)
		Node* _Copy(const Node* root);
		//后序遍历销毁二叉树
		void _Destroy(Node*& root);
		//递归实现插入节点
		bool _InsertR(Node*& root, const K& key,const V& value);
		//递归实现节点查找
		Node * _FindR(Node*& root, const K& key);
		//递归删除搜索树结点
		bool _EraseR(Node*& root, const K& key);
		//非递归删除搜索树结点
		bool _Erase(Node *&root,const K& key)//中序遍历搜索树
		void _InOrder(Node* root);
		//寻找子树key值最大的结点的接口
		Node* _FindMax(Node* root);
	private:
		//根节点指针
		Node* _root = nullptr;
	};

2. 二分探索木のノード削除

  • 検索ツリー ノードの削除操作では、二分検索ツリーの構造プロパティを変更しないでください。
  • 検索ツリー ノードを非再帰的に削除する
    • インターフェイスヘッダー: bool _Erase(Node*& root, const K& key)(削除が成功した場合は true を返し、それ以外の場合は false を返します)

    • コードロジックのマインドマップ: ここに画像の説明を挿入

    • ケース 1 のデモ:ここに画像の説明を挿入

    • 削除ケース 3 のデモ:ここに画像の説明を挿入

    • 削除ケース 2 のデモ:ここに画像の説明を挿入

    • ケース2の場合サブツリーの最上位値の置換および削除方法約束探索木の構造特性そして削除してくださいシンプルさと論理的な独自性

    • 非再帰的な削除ノード コード:

		//非递归删除节点
		bool _Erase(Node*& root, const K& key)
		{
    
    
			Node* precur = nullptr;
			Node* cur = root;
			while (cur)
			{
    
    
				if (cur->_key > key)
				{
    
    
					precur = cur;
					cur = cur->_left;
				}
				else if (cur->_key < key)
				{
    
    
					precur = cur;
					cur = cur->_right;
				}
				else
				{
    
    
					//执行删除操作
					//找到待删除节点
					//待删除结点只可能有右孩子
					if (cur->_left == nullptr)
					{
    
    
						//判断待删结点是否为根节点
						if (root == cur)
						{
    
    
							root = cur->_right;
						}
						else
						{
    
    
							//将右孩子交给前驱指针
							if (precur->_left == cur)
							{
    
    
								precur->_left = cur->_right;
							}
							else
							{
    
    
								precur->_right = cur->_right;
							}
						}
						delete cur;
						cur = nullptr;
						return true;
					}
					//待删除结点只有左孩子
					else if (cur->_right == nullptr)
					{
    
    
						//判断待删结点是否为根节点
						if (root == cur)
						{
    
    
							root = cur->_left;
						}
						else
						{
    
    
							//将左孩子交给前驱指针
							if (precur->_left == cur)
							{
    
    
								precur->_left = cur->_left;
							}
							else
							{
    
    
								precur->_right = cur->_left;
							}
						}
						delete cur;
						cur = nullptr;
						return true;
					}
					//待删除结点既有左孩子又有右孩子
					else
					{
    
    
						//用左子树的最大结点与待删节点进行替换后,再删除待删节点
						Node* Maxpre = cur;
						Node* Max = cur->_left;
						while (Max->_right)
						{
    
    
							Maxpre = Max;
							Max = Max->_right;
						}
						std::swap(Max->_key, cur->_key);
						//将左孩子交给前驱指针
						if (Maxpre->_left == Max)
						{
    
    
							Maxpre->_left = Max->_left;
						}
						else
						{
    
    
							Maxpre->_right = Max->_left;
						}
						delete Max;
						Max = nullptr;
						return true;
					}
				}
			}
			return false;
		}
  • 検索ツリー ノードを再帰的に削除します。
    • インターフェースヘッダーbool _EraseR(Node*& root, const K& key)
    • 検索ツリーのノードを再帰的に削除する使用されるアルゴリズムノードポインタ変数への参照パラメータ渡しの再帰を実行し、仮パラメータを直接参照します現在操作対象のノードプリカーサーポインター, したがって、関数内で次のことができますパラメータを介して直接削除するノードの先行ポインタを変更します、コードの記述がはるかに簡単になります
    • 検索ツリー ノード コードを再帰的に削除します。
		//寻找子树最大结点的接口
		Node* _FindMax(Node*root)
		{
    
    
			while (root->_right)
			{
    
    
				root = root->_right;
			}
			return root;
		}
		bool _EraseR(Node*& root, const K& key)
		{
    
    
			if (root == nullptr)
			{
    
    
				return false;
			}
			else if (root->_key > key)
			{
    
    
				//在左子树中删除节点
				_EraseR(root->_left, key);
			}
			else if (root->_key < key)
			{
    
    
				//在右子树中删除节点
				_EraseR(root->_right, key);
			}
			else
			{
    
    
				Node* del = root;
				//对待删除节点进行分类讨论,同样分三类进行讨论
				if (root->_left == nullptr)
				{
    
    
					//将右子树交给前驱指针
					root = root->_right;
				}
				else if (root->_right == nullptr)
				{
    
    
					//将左子树交给前驱指针
					root = root->_left;
				}
				else
				{
    
    
					Node* Max = _FindMax(root->_left);
					std::swap(Max->_key, root->_key);
					//递归删除换位后的目标节点
					_EraseR(root->_left, key);
					return true;
				}
				delete del;
				return true;
			}
		}

3. 二分探索木オブジェクトのその他のインターフェース

コンストラクター、デストラクター、代入演算子のオーバーロード

  • 補助インターフェースNode* _Copy(const Node* root)
  • コピーコンストラクターBSTree(const BSTree<K,V>& t)
		//强制编译器生成类的默认构造函数
		BSTree() = default;
		//用于实现拷贝构造的辅助函数(前序递归实现树的深拷贝)
		Node* _Copy(const Node* root)
		{
    
    
			if (root == nullptr)
			{
    
    
				return nullptr;
			}
			Node* Root = new Node(root->_key,root->_value);
			Root->_left = _Copy(root->_left);
			Root->_right = _Copy(root->_right);
			return Root;
		}
		BSTree(const BSTree<K,V>& t)
		{
    
    
			_root = _Copy(t._root);
		}
  • 代入演算子のオーバーロード:
    • インターフェースヘッダー: BSTree<K,V>& operator =(BSTree<K,V>t)、経由再利用可能なコピー構築の実装
		//通过复用拷贝构造实现赋值运算符重载
		BSTree<K,V>& operator =(BSTree<K,V>t)
		{
    
    
			std::swap(t._root, _root);
			return (*this);
		}

ここに画像の説明を挿入

  • デストラクター:
    • ポストオーダートラバーサルは再帰的にすべてのノードを解放します
    • 補助インターフェース:void _Destroy(Node*& root);
		//后序遍历销毁二叉树
		void _Destroy(Node*& root)
		{
    
    
			if (root == nullptr)
			{
    
    
				return;
			}
			_Destroy(root->_left);
			_Destroy(root->_right);
			delete root;
			root = nullptr;
		}
		//搜索树的析构函数
		~BSTree()
		{
    
    
			_Destroy(_root);
		}

ノード挿入インターフェースとノード検索インターフェース

  • ノード挿入インターフェイス
    • 補助インターフェースbool _InsertR(Node*& root, const K& key,const V& value);、再帰によるノードの挿入
    • 外部呼び出し用のインターフェース:bool InsertR(const K& key, const V& value);
		//递归插入节点
		bool _InsertR(Node*& root, const K& key,const V& value)
		{
    
    
			if (root == nullptr)
			{
    
    
				//找到空位置则插入
				root = new Node(key,value);
				return true;
			}
			else if (root->_key > key)
			{
    
    
				return _InsertR(root->_left, key,value);
			}
			else if (root->_key < key)
			{
    
    
				return _InsertR(root->_right, key,value);
			}
			else
			{
    
    
				//找到相同节点则插入失败
				//插入操作中体现了搜索树的去重功能
				return false;
			}
		}
		//插入节点
		bool InsertR(const K& key, const V& value)
		{
    
    
			return _InsertR(_root, key, value);
		}
  • ノード検索インターフェース
    • 再帰的な実装:
		//递归实现查找
		Node * _FindR(Node*& root, const K& key)
		{
    
    
			if (root == nullptr)
			{
    
    
				//找到空则说明结点不存在
				return nullptr;
			}
			else if (root->_key > key)
			{
    
    
				return _FindR(root->_left,key);
			}
			else if (root->_key < key)
			{
    
    
				return _FindR(root->_right,key);
			}
			else
			{
    
    
				return root;
			}
		}
		//查找节点
		Node* Find(const K& key)
		{
    
    
			return _FindR(_root, key);
		}

Key_value モデルバイナリ検索ツリー クラス テンプレート全体のコード

namespace key_value
{
    
    
#pragma once
	template<class K, class V>
	struct BSTreeNode
	{
    
    
		BSTreeNode<K, V>* _left;
		BSTreeNode<K, V>* _right;
		K _key;
		V _value;
		BSTreeNode(const K& key = K(), const V& value = V())
			:_left(nullptr)
			, _right(nullptr)
			, _key(key)
			, _value(value)
		{
    
    }
	};


	template<class K, class V>
	class BSTree
	{
    
    
		typedef BSTreeNode<K, V> Node;
	public:
		//强制生成类的默认构造函数
		BSTree() = default;
		//利用前序遍历递归完成拷贝构造
		BSTree(const BSTree<K,V>& t)
		{
    
    
			_root = _Copy(t._root);
		}
		//通过复用拷贝构造实现赋值运算符重载
		BSTree<K,V>& operator =(BSTree<K,V>t)
		{
    
    
			std::swap(t._root, _root);
			return (*this);
		}
		//插入节点
		bool InsertR(const K& key, const V& value)
		{
    
    
			return _InsertR(_root, key, value);
		}
		//查找节点
		Node* Find(const K& key)
		{
    
    
			return _FindR(_root, key);
		}
		//删除节点
		bool Erase(const K& key)
		{
    
    
			return _EraseR(_root, key);
		}
		//中序遍历搜索树
		void InOrder()
		{
    
    
			_InOrder(_root);
		}
		//搜索树的析构函数
		~BSTree()
		{
    
    
			_Destroy(_root);
		}
	protected:
		//用于实现拷贝构造的辅助函数(前序递归实现树的深拷贝)
		Node* _Copy(const Node* root)
		{
    
    
			if (root == nullptr)
			{
    
    
				return nullptr;
			}
			Node* Root = new Node(root->_key,root->_value);
			Root->_left = _Copy(root->_left);
			Root->_right = _Copy(root->_right);
			return Root;
		}
		//后序遍历销毁二叉树
		void _Destroy(Node*& root)
		{
    
    
			if (root == nullptr)
			{
    
    
				return;
			}
			_Destroy(root->_left);
			_Destroy(root->_right);
			delete root;
			root = nullptr;
		}
		//递归插入节点
		bool _InsertR(Node*& root, const K& key,const V& value)
		{
    
    
			if (root == nullptr)
			{
    
    
				//找到空位置则插入
				root = new Node(key,value);
				return true;
			}
			else if (root->_key > key)
			{
    
    
				return _InsertR(root->_left, key,value);
			}
			else if (root->_key < key)
			{
    
    
				return _InsertR(root->_right, key,value);
			}
			else
			{
    
    
				//找到相同节点则插入失败
				return false;
			}
		}
		//递归实现查找
		Node * _FindR(Node*& root, const K& key)
		{
    
    
			if (root == nullptr)
			{
    
    
				//找到空则说明结点不存在
				return nullptr;
			}
			else if (root->_key > key)
			{
    
    
				return _FindR(root->_left,key);
			}
			else if (root->_key < key)
			{
    
    
				return _FindR(root->_right,key);
			}
			else
			{
    
    
				return root;
			}
		}
		//非递归删除节点
		bool _Erase(Node*& root, const K& key)
		{
    
    
			Node* precur = nullptr;
			Node* cur = root;
			while (cur)
			{
    
    
				if (cur->_key > key)
				{
    
    
					precur = cur;
					cur = cur->_left;
				}
				else if (cur->_key < key)
				{
    
    
					precur = cur;
					cur = cur->_right;
				}
				else
				{
    
    
					//执行删除操作
					//找到待删除节点
					//待删除结点只可能有右孩子
					if (cur->_left == nullptr)
					{
    
    
						//判断待删结点是否为头节点
						if (root == cur)
						{
    
    
							root = cur->_right;
						}
						else
						{
    
    
							if (precur->_left == cur)
							{
    
    
								precur->_left = cur->_right;
							}
							else
							{
    
    
								precur->_right = cur->_right;
							}
						}
						delete cur;
						cur = nullptr;
						return true;
					}
					//待删除结点只有左孩子
					else if (cur->_right == nullptr)
					{
    
    
						//判断待删结点是否为头节点
						if (root == cur)
						{
    
    
							root = cur->_left;
						}
						else
						{
    
    
							if (precur->_left == cur)
							{
    
    
								precur->_left = cur->_left;
							}
							else
							{
    
    
								precur->_right = cur->_left;
							}
						}
						delete cur;
						cur = nullptr;
						return true;
					}
					//待删除结点既有左孩子又有右孩子
					else
					{
    
    
						//用左子树的最大结点与待删节点进行替换后,再删除待删节点
						Node* Maxpre = cur;
						Node* Max = cur->_left;
						while (Max->_right)
						{
    
    
							Maxpre = Max;
							Max = Max->_right;
						}
						std::swap(Max->_key, cur->_key);
						if (Maxpre->_left == Max)
						{
    
    
							Maxpre->_left = Max->_left;
						}
						else
						{
    
    
							Maxpre->_right = Max->_left;
						}
						delete Max;
						Max = nullptr;
						return true;
					}
				}
			}
			return false;
		}
		//递归删除搜索树结点
		bool _EraseR(Node*& root, const K& key)
		{
    
    
			if (root == nullptr)
			{
    
    
				return false;
			}
			else if (root->_key > key)
			{
    
    
				_EraseR(root->_left, key);
			}
			else if (root->_key < key)
			{
    
    
				_EraseR(root->_right, key);
			}
			else
			{
    
    
				Node* del = root;
				if (root->_left == nullptr)
				{
    
    
					root = root->_right;
				}
				else if (root->_right == nullptr)
				{
    
    
					root = root->_left;
				}
				else
				{
    
    
					Node* Max = _FindMax(root->_left);
					std::swap(Max->_key, root->_key);
					_EraseR(root->_left, key);
					return true;
				}
				delete del;
				return true;
			}
		}
		//中序遍历搜索树
		void _InOrder(Node* root)
		{
    
    
			if (root == nullptr)
			{
    
    
				return;
			}
			_InOrder(root->_left);
			cout << "结点的key值:" << root->_key << ' ' << "节点的value:" << root->_value << endl;
			_InOrder(root->_right);
		}
		//寻找子树最大结点的接口
		Node* _FindMax(Node* root)
		{
    
    
			while (root->_right)
			{
    
    
				root = root->_right;
			}
			return root;
		}
	private:
		Node* _root = nullptr;
	};
}

4. 不平衡二分探索木の欠陥

  • 二分探索ツリーにおけるノードの挿入、削除、および検索操作の時間計算量は、次によって異なります。検索木の高さ
  • 同じノード数でも、完全な二分木の高さは最も小さい、検索ツリーが完全なバイナリ ツリーの場合、ノードの挿入、削除、および検索操作時間の複雑さどちらも O(logN)
  • しかし、同じノードのセット、構築された二分探索木の高さは不確実です (場合によっては、単一リンク リストの線形形状さえも直接構築され、この時点で探索木は線形リストに縮退します)。次に例を示します。ここに画像の説明を挿入
  • したがって、二分探索木を実際のシーンで役割を果たすには、それを最適化するためのバランシング アルゴリズムが必要です (いわゆるバランシングとは、探索木の構造を変更することです)完全な二分木に近づける)
    ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/weixin_73470348/article/details/131145159