[C++] 이진 검색 트리(K, KV 트리) 재귀적 및 비재귀적 방법의 시뮬레이션 구현


머리말

여기에 이미지 설명을 삽입하세요.

1. K-트리

K 모델: K 모델은 키 코드로 키만 가지고 있으며, 키만 구조에 저장하면 되며, 키 코드는 검색해야 하는 값입니다.

1. 노드의 정의

template<class K>
struct BSTreeNode
{
    
    
	BSTreeNode<K>* _left;
	BSTreeNode<K>* _right;
	K _key;

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

2. 생성자

template<class K>
class BSTree {
    
    
	typedef BSTreeNode<K> Node;
public:
	BSTree() {
    
    
		_root = nullptr;
	}

3. 복사 생성자

BSTree(const BSTree<K>& t) {
    
    
		_root = Copy(t._root);
	}
Node* Copy(Node* root) {
    
    
		if (root == nullptr) {
    
    
			return nullptr;
		}
		//递归进行拷贝
		Node* copyroot = new Node(root->_key);
		copyroot->_left = Copy(root->_left);
		copyroot->_right = Copy(root->_right);
		return copyroot;
	}

4. 할당 연산자 오버로딩

BSTree<K>& operator=(BSTree<K> t) {
    
    
//先拷贝出t,让_root指向t的位置,进行交换
//后续调用析构函数直接析构t._root
		swap(_root, t._root);
		return *this;
	}

5. 소멸자

~BSTree() {
    
    
		Destroy(_root);
	}
	void Destroy(Node*& root) {
    
    
		if (root == nullptr) {
    
    
			return;
		}
		Destroy(root->_left);
		Destroy(root->_right);
		delete root;
		root = nullptr;
	}

6. 이진 검색 트리 검색(찾기)

이진 검색 트리 검색
a. 루트부터 비교 검색을 시작하여 루트보다 크면 오른쪽으로 검색하고, 루트보다 작으면 왼쪽으로 검색합니다.
b.높이를 검색하는 경우가 대부분이며 비어 있으면 아직 발견되지 않은 값입니다. 이 값은 존재하지 않습니다.

1. 비재귀적

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;
	 }

2. 재귀

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;
		}
	}

7. 이진 검색 트리 삽입(Insert)

.이진 검색 트리에 삽입.
삽입의 구체적인 과정은 다음과 같습니다.a
.트리가 비어 있으면 노드를 직접 추가하고 루트 포인터에 할당합니다.b
.트리가 비어 있지 않으면 삽입 위치를 찾습니다. 이진 검색 트리의 속성에 따라 새 노드를 삽입합니다.

1. 비재귀적

bool Insert(const K& key) {
    
    
		if (_root == nullptr) {
    
    
			_root = new Node(key);
			//树为空先建立结点
			return true;
		}
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur) {
    
    //寻找要插入位置
			if (cur->_key < key) {
    
    
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key) {
    
    
				parent = cur;
				cur = cur->_left;
			}
			else {
    
    
				//树中已经有key值不能再插入
				return false;
			}
		}
		cur = new Node(key);//cur为要插入结点
		if (parent->_key < key) {
    
    
			//判断cur插在父结点的左数还是右树
			parent->_right = cur;
		}
		else {
    
    
			parent->_left = cur;
		}
		return true;//插入成功
	 }

2. 재귀

여기에 이미지 설명을 삽입하세요.

bool InsertR(const K&key) {
    
    
		return _InsertR(_root, key);
	}
	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;}
	}

8. 이진 검색 트리 삭제(Erase)

먼저 이진 검색 트리에 해당 요소가 있는지 확인하고, 존재하지 않으면 반환하고, 그렇지 않으면 삭제되는 노드는 다음 4가지 상황으로 나눌 수
있습니다
. b.삭제할 노드는 왼쪽 자식 노드만
c.삭제할 노드는 오른쪽 자식 노드만
d.삭제할 노드는 왼쪽과 오른쪽 자식 노드입니다.
4가지 상황이 있는 것 같습니다. 실제 상황 a는 상황 b 또는 c와 병합될 수 있으므로 실제 삭제 프로세스는 다음과 같습니다.
사례
b: 노드를 삭제하고 삭제된 노드의 상위 노드가 왼쪽 하위 노드를 가리키도록 합니다. 삭제된 노드의 - 직접 삭제
사례 c: 노드를 삭제하고 삭제된 노드의 부모 노드가 삭제된 노드의 왼쪽 자식 노드를 가리키도록 함 부모 노드가 삭제된 노드의 오른쪽 자식 노드를 가리킴 - 직접 삭제 사례 d
: 오른쪽 하위 트리에서 중간 순서(가장 작은 키 코드)의 첫 번째 노드를 찾아 삭제될 때까지 해당 노드에 해당 값을 채운 후 노드 교체 방법 삭제의 삭제 문제를 처리합니다.

여기에 이미지 설명을 삽입하세요.

여기에 이미지 설명을 삽입하세요.

1. 비재귀적

	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.   要删除节点左边为空
				if (cur->_left == nullptr) {
    
    
					if (cur == _root) {
    
    
						//要删除结点为根节点,则改变根节点位置
						_root = _root->_right;
					}
					else {
    
    
						//判断要删除结点在父结点的左子树还是右子树
						if (parent->_right == cur) {
    
    
							//在父结点右子树则让其指向删除结点的右子树
							parent->_right = cur->_right;
						}
						else {
    
    
							parent->_left = cur->_right;
						}
					}
				}
				//2.     要删除节点右边为空
				else if (cur->_right == nullptr) {
    
    
					if (cur == _root) {
    
    
						//要删除结点为根节点,则改变根节点位置
						_root = _root->_left;
					}
					else {
    
    
						//判断要删除结点在父结点的左子树还是右子树
						if (parent->_right == cur) {
    
    
							parent->_right = cur->_left;
						}
						else {
    
    
							parent->_left = cur->_left;
						}
					}
				}
				else {
    
    //3.要删除结点左右子树都不为空
					Node* parent = cur;
					Node* leftMax = cur->_left;
					//寻找可替代结点,替代过好要满足二叉搜索树的性质
					//要删除结点左子树的最大值,或者右子树的最小值
					while (leftMax->_right) {
    
    
						//寻找左子树的最大值
						parent = leftMax;
						leftMax = leftMax->_right;
					}
					swap(leftMax->_key, cur->_key);
					//替代结点与被删除结点的值交换
					//这样leftMax就为要被删除的结点
					if (parent->_left == leftMax) {
    
    
						//判断leftMax在父结点的左子树还是右子树
						parent->_left = leftMax->_left;
					}
					else {
    
    
						parent->_right = leftMax->_left;
					}//改变指向
					cur = leftMax;
				}
				delete cur;
				return true;
			}	
		}
		return false;
 }

2. 재귀

bool EraseR(const K&key) {
    
    
		return _EraseR(_root, key);
	}

bool _EraseR(Node*& root, const K& key) {
    
    
		//这里为结点指针的引用,可以不需要父结点直接修改
		if (root == nullptr) {
    
    
			return false;
		}
		if (root->_key < key) {
    
    
			return _Erase(root->_right, key);
		}
		else if (root->_key > key) {
    
    
			return _Erase(root->_left, key);
		}//寻找要删除结点
		else {
    
    
			Node* del = root;
			//1. 要删除节点左边为空
			if (root->_left == nullptr) {
    
    
				root = root->_right;
			}
			//2. 要删除节点右边为空
			else if (root->_right == nullptr) {
    
    
				root = root->_left;
			}
			else {
    
    
				//3.要删除结点左右都不为空
				Node* leftMax = root->_left;
				while (leftMax->_right) {
    
    
					//寻找替代结点
					leftMax = leftMax->_right;
				}
				swap(root->_key, leftMax->_key);
				//交换值后,要删除的结点为leftMax,其在root的左子树
				return _Erase(root->_left, key);
			}
			delete del;
			return true;
		}
	}

9. 중위순회(InOrder)

void InOrder() {
    
    
		_InOrder(_root);
		cout << endl;
	}
void _InOrder(Node* root) {
    
    
		if (root == nullptr) {
    
    
			return;
		}
		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}

2. KV 트리

KV 모델: 각 키 키에는 해당 값 Value가 있습니다. 즉, <Key, Value>의 키-값 쌍입니다.

namespace key_value {
    
    
	template<class K, class V>
	struct BSTreeNode
	{
    
    
		BSTreeNode<K, V>* _left;
		BSTreeNode<K, V>* _right;
		K _key;
		V _value;

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

	template<class K, class V>
	class BSTree {
    
    
		typedef BSTreeNode<K, V> Node;
	public:
		BSTree() {
    
    
			_root = nullptr;
		}

		BSTree(const BSTree<K, V>& t) {
    
    
			_root = Copy(t._root);
		}

		BSTree<K, V>& operator=(BSTree<K, V> t) {
    
    
			swap(_root, t._root);
			return *this;
		}

		~BSTree() {
    
    
			Destroy(_root);
		}



		bool InsertR(const K& key, const V& value) {
    
    
			return _InsertR(_root, key, value);
		}

		bool EraseR(const K& key) {
    
    
			return _Erase(_root, key);
		}

		Node* FindR(const K& key) {
    
    
			return 	_FindR(_root, key);
		}
		void InOrder() {
    
    
			_InOrder(_root);
			cout << endl;
		}



	private:
		Node* Copy(Node* root) {
    
    
			if (root == nullptr) {
    
    
				return nullptr;
			}
			Node* copyroot = new Node(root->_key);
			copyroot->_left = Copy(root->_left);
			copyroot->_right = Copy(root->_right);
			return copyroot;
		}

		void Destroy(Node*& root) {
    
    
			if (root == nullptr) {
    
    
				return;
			}
			Destroy(root->_left);
			Destroy(root->_right);
			delete root;
			root = nullptr;
		}

		Node* _FindR(Node* root, const K& key) {
    
    
			if (root == nullptr) {
    
    
				return nullptr;
			}
			if (root->_key < key) {
    
    
				return _FindR(root->_right, key);
			}
			else if (root->_key > key) {
    
    
				return _FindR(root->_left, key);
			}
			else {
    
    
				return root;
			}

		}

		bool _InsertR(Node*& root, const K& key, const V& value) {
    
    
			if (root == nullptr) {
    
    
				root = new Node(key, value);
				return true;
			}
			if (root->_key < key) {
    
    
				return _InsertR(root->_right, key, value);
			}
			else if (root->_key > key) {
    
    
				return _InsertR(root->_left, key, value);
			}
			else {
    
    
				return false;
			}
		}

		bool _Erase(Node*& root, const K& key) {
    
    
			if (root == nullptr) {
    
    
				return false;
			}
			if (root->_key < key) {
    
    
				return _Erase(root->_right, key);
			}
			else if (root->_key > key) {
    
    
				return _Erase(root->_left, key);
			}
			else {
    
    
				Node* del = root;
				if (root->_left == nullptr) {
    
    
					root = root->_right;
				}
				else if (root->_right == nullptr) {
    
    
					root = root->_left;
				}
				else {
    
    
					Node* leftMax = root->_left;
					while (leftMax->_right) {
    
    
						leftMax = leftMax->_right;
					}
					swap(root->_key, leftMax->_key);
					return _Erase(root->_left, key);
				}
				delete del;
				return true;
			}
		}


		void _InOrder(Node* root)
		{
    
    
			if (root == NULL){
    
    return;}
			_InOrder(root->_left);
			cout << root->_key << ":" << root->_value << endl;
			_InOrder(root->_right);
		}

	private:
		Node* _root;
	};
}

이진 검색 트리 성능

여기에 이미지 설명을 삽입하세요.

가장 높은 검색 높이 O(N)

추천

출처blog.csdn.net/m0_74774759/article/details/132210067