Implementación de simulación de mapa y conjunto | Uso de árbol rojo-negro para encapsular mapa y conjunto | Análisis de código fuente STL

prefacio

那么这里博主先安利一些干货满满的专栏了!

首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助。

高质量干货博客汇总icon-default.png?t=N6B9https://blog.csdn.net/yu_cblog/category_12379430.html?spm=1001.2014.3001.5482这两个都是博主在学习Linux操作系统过程中的记录,希望对大家的学习有帮助!

操作系统Operating Syshttps://blog.csdn.net/yu_cblog/category_12165502.html?spm=1001.2014.3001.5482Linux Sys https://blog.csdn.net/yu_cblog/category_11786077.html?spm=1001.2014.3001.5482 Estas dos son columnas para que los bloggers aprendan estructuras de datos y simulen varios contenedores en la biblioteca de plantillas estándar STL.

Análisis de código fuente STL https://blog.csdn.net/yu_cblog/category_11983210.html?spm=1001.2014.3001.5482 estructura de datos desgarrada a mano https://blog.csdn.net/yu_cblog/category_11490888.html


¿Cuál es la capa inferior de Map and Set?

En C ++ STL, mapy setla capa inferior generalmente se implementa utilizando un árbol rojo-negro (Árbol rojo-negro).

Un árbol rojo-negro es un árbol de búsqueda binaria autoequilibrado que mantiene el equilibrio almacenando información adicional (color) en cada nodo. Estos mensajes de color siguen un conjunto específico de reglas para garantizar que el árbol permanezca en un estado equilibrado. La naturaleza equilibrada de los árboles rojo-negro hace que las operaciones de inserción, eliminación y búsqueda sean logarítmicas en complejidad temporal.

En un árbol rojo-negro, cada nodo tiene un par clave-valor, clasificado en el orden de las claves. Para map, cada nodo también tiene un valor asociado, y para set, la clave de un nodo es su valor. Esta naturaleza ordenada lo hace mapmuy setadecuado para escenarios de aplicaciones que necesitan ser buscados, insertados y eliminados por clave.

Cabe señalar que aunque el árbol rojo-negro es una implementación subyacente común de mapy set, la implementación específica puede variar con diferentes compiladores e implementaciones de biblioteca estándar. Por lo tanto, la implementación subyacente puede variar para compiladores específicos y versiones de biblioteca estándar.

Por lo tanto, antes de aprender a usar el árbol rojo-negro para encapsular el mapa y el conjunto de STL, debemos aprender el principio subyacente del árbol rojo-negro. Puede ver el blog anterior del blogger, que habla sobre el principio del árbol rojo-negro. árbol rojo-negro muy bien detallado.

Árbol rojo-negro desgarrado a mano | Cambio de color + rotación ¿Realmente entiendes? [Explicación gráfica súper intencional y súper detallada | Un artículo para aprender Red_Black_Tree] icono-predeterminado.png?t=N6B9https://blog.csdn.net/Yu_Cblog/article/details/128210260

Sin embargo, el árbol rojo-negro del blog anterior es ligeramente diferente de la versión que se empaquetará.

Porque el conjunto solo necesita la clave, y el mapa es una estructura clave-valor.

El blogger de la versión empaquetada del archivo de encabezado de árbol rojo y negro se coloca al final del blog.

encapsulación

Mapa.h

#pragma once

#include"RBTree.h"

namespace ns_Map {
	template<class K, class V>
	class map {
		struct MapKeyOfT {
			const K& operator()(const pair<K, V>& kv) {
				return kv.first;
			}
		};
	public:
	//迭代器
		//取模板的模板的类型 -- 所以要typename -- 告诉编译器是类型
		typedef typename RBTree <K, pair<K, V>, MapKeyOfT>::iterator iterator;
		iterator begin() {
			return _t.begin();
		}
		iterator end() {
			return _t.end();
		}
	public:
		//operator[] -- 只有map才有
		V& operator[](const K& key) {
			//如果有 -- 插入成功
			//如果没有 -- 插入失败
			pair<iterator, bool>ret = insert(make_pair(key, V()));
			return ret.first->second;
		}
	public:
		pair<iterator, bool> insert(const pair<K, V>& kv) {
			return _t.insert(kv);
		}
	private:
		RBTree <K, pair<K, V>, MapKeyOfT>_t;
	};



	void test_map() {
		map<int, int>m;
		m.insert(make_pair(1, 2));
		m.insert(make_pair(5, 2));
		m.insert(make_pair(2, 2));
		m.insert(make_pair(2, 2));
		m.insert(make_pair(3, 2));
		m.insert(make_pair(4, 2));
		m.insert(make_pair(6, 2));

		cout << "测试++" << endl;
		map<int, int>::iterator it = m.begin();
		while (it != m.end()) {
			cout << it->first << endl;
			++it;
		}
		cout << "测试--" << endl;
		//--it;
		//while (it != m.begin()) {
		//	cout << it->first << endl;
		//	--it;
		//}
		cout << endl;
	}
	void test_map2() {
		string arr[] = { "苹果","香蕉","苹果","苹果","西瓜","香蕉","苹果","苹果" };
		map<string, int>hash;
		for (auto& str : arr) {
			hash[str]++;
		}
		//map<string, int>::iterator it = hash.begin();
		//while (it != hash.end()) {
		//	cout << it->first << ":" << it->second << endl;
		//	++it;
		//}
		//范围for
		for (auto& e : hash) {
			cout << e.first << ":" << e.second << endl;
		}
	}
}

Establecer.h

#pragma once

#include"RBTree.h"

namespace ns_Set{
	template<class K>
	class set {
		struct SetKeyOfT {
			const K& operator()(const K& key) {
				return key;
			}
		};
	public:
		//µü´úÆ÷
		typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator;
		iterator begin() {
			return _t.begin();
		}
		iterator end() {
			return _t.end();
		}
	public:
		pair<iterator, bool> insert(const K& key) {
			return _t.insert(key);
		}
	private:
		RBTree<K, K, SetKeyOfT>_t;
	};





	void test_set() {
		set<int>s;
		s.insert(3);
		s.insert(3);
		s.insert(1);
		s.insert(2);
		s.insert(5);
		s.insert(5);
		s.insert(7);
		s.insert(6);

		set<int>::iterator it = s.begin();
		while (it != s.end()) {
			cout << *it << endl;
			++it;
		}
		cout << endl;
	}
}

RBÁrbol.h

#pragma once
#include<map>
#include<iostream>
#include<assert.h>
using namespace std;
enum Colour {
	RED,BLACK
};

template<class T>
struct __Red_Black_TreeNode {
	__Red_Black_TreeNode<T>* _left;
	__Red_Black_TreeNode<T>* _right;
	__Red_Black_TreeNode<T>* _parent;
	//pair<K, V>_kv;
	T _data;
	Colour _col;
	__Red_Black_TreeNode(const T& data)
		:_left(nullptr), _right(nullptr), _parent(nullptr), _data(data) {}
};

//迭代器
template<class T, class Ref, class Ptr>
struct __redblack_tree_iterator {
	typedef __Red_Black_TreeNode<T>Node;
	Node* _node;
	__redblack_tree_iterator(Node* node)
		:_node(node) {}
	Ref operator*() {
		return _node->_data;
	}
	Ptr operator->() {
		return &_node->_data;
	}
	bool operator!=(const __redblack_tree_iterator& s) const {
		return _node != s._node;
	}
	bool operator==(const __redblack_tree_iterator& s) const {
		return _node == s._node;
	}
	__redblack_tree_iterator& operator++() {
		//1.右子树不为空,++就是找右子树的中序第一个
		//2.如果右子树为空,找孩子不是父亲右边的那个祖先
			//如果当前节点是父亲的右,说明父亲访问过了
		if (_node->_right) {
			//1.
			Node* left = _node->_right;
			while (left->_left) {
				left = left->_left;
			}
			_node = left;
		}
		else {
			//2.
			Node* parent = _node->_parent;
			Node* cur = _node;
			while (parent && cur == parent->_right) {
				cur = cur->_parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}
	__redblack_tree_iterator& operator--() {
		//1.如果左不为空 -- 找左的最右
		//2.如果左为空 -- 找到孩子不是父亲的左的那个祖先
		if (_node->_left) {
			Node* right = _node->_left;
			while (right->_right) {
				right = right->_right;
			}
			_node = right;
		}
		else {
			Node* parent = _node->_parent;
			Node* cur = _node;
			while (parent && cur == parent->_left) {
				cur = cur->_parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}	
};


//第三个模板参数是一个仿函数
template<class K,class T,class KeyOfT>
struct RBTree {
	typedef __Red_Black_TreeNode<T>Node;
private:
	Node* _root = nullptr;
public:
	typedef __redblack_tree_iterator<T, T&, T*> iterator;
	iterator begin() {
		//stl源码里面是有哨兵位的,这里我们没有 -- 所以我们找一下最左节点
		Node* left = _root;
		while (left && left->_left) {
			left = left->_left;
		}
		return iterator(left);
	}
	iterator end() {
		return iterator(nullptr);//直接给空就行了
	}
private:
	//左单旋
	void rotate_left(Node* parent) {
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		parent->_right = subRL;
		if (subRL) {
			subRL->_parent = parent;
		}
		Node* ppNode = parent->_parent;//记录一下原先parent的parent
		subR->_left = parent;
		parent->_parent = subR;
		if (_root == parent) {
			_root = subR;
			subR->_parent = nullptr;
		}
		else {
			//如果ppNode==nullpt,是不会进来这里的
			if (ppNode->_left == parent) {
				ppNode->_left = subR;
			}
			else {
				ppNode->_right = subR;
			}
			subR->_parent = ppNode;
		}
	}
	//右单旋
	void rotate_right(Node* parent) {
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		parent->_left = subLR;
		if (subLR) {
			subLR->_parent = parent;
		}
		Node* ppNode = parent->_parent;
		subL->_right = parent;
		parent->_parent = subL;
		if (_root == parent) {
			_root = subL;
			subL->_parent = nullptr;
		}
		else {
			if (ppNode->_left == parent) {
				ppNode->_left = subL;
			}
			else {
				ppNode->_right = subL;
			}
			subL->_parent = ppNode;
		}
	}
public:
	//前面插入的过程和搜索树一样的
	pair<iterator, bool> insert(const T& data) {
		KeyOfT kot;
		if (_root == nullptr) {
			_root = new Node(data);
			_root->_col = BLACK;
			return make_pair(iterator(_root), true);
		}
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur) {
			if (kot(cur->_data) < kot(data)) {
				parent = cur;
				cur = cur->_right;
			}
			else if (kot(cur->_data) > kot(data)) {
				parent = cur;
				cur = cur->_left;
			}
			else return make_pair(iterator(cur), false);
		}
		cur = new Node(data);
		Node* newnode = cur;
		cur->_col = RED;//一开始尽量先变红
		if (kot(parent->_data) < kot(data)) {
			parent->_right = cur;
		}
		else {
			parent->_left = cur;
		}
		cur->_parent = parent;

		while (parent && parent->_col == RED) {
			Node* grandparent = parent->_parent;
			assert(grandparent && grandparent->_col == BLACK);
			//关键看叔叔
			//判断一下左右
			if (parent == grandparent->_left) {
				Node* uncle = grandparent -> _right;
				//情况1(不看方向)
				if (uncle && uncle->_col == RED) {
					parent->_col = uncle->_col = BLACK;
					grandparent->_col = RED;
					//继续向上处理
					cur = grandparent;
					parent = cur->_parent;
				}
				//情况2+3
				//uncle不存在/存在且为黑
				else {
					//情况2
					//   g
					//  p  u
					// c
					//右单旋+变色
					if (cur == parent->_left) {
						rotate_right(grandparent);
						parent->_col = BLACK;//父亲变黑
						grandparent->_col = RED;//祖父变红
					}
					//情况3
					//   g
					//  p  u
					//   c
					//左右双旋+变色
					else {
						rotate_left(parent);
						rotate_right(grandparent);
						//看着图写就行了
						cur->_col = BLACK;
						grandparent->_col = RED;
					}
					break;
				}
			}
			else {
				Node* uncle = grandparent->_left;
				//情况1(不看方向)
				if (uncle && uncle->_col == RED) {
					parent->_col = uncle->_col = BLACK;
					grandparent->_col = RED;
					//继续向上处理
					cur = grandparent;
					parent = cur->_parent;
				}
				else {
					//情况2
					//   g
					//  u  p
					//      c 
					//左单旋+变色
					if (cur == parent->_right) {
						rotate_left(grandparent);
						parent->_col = BLACK;//父亲变黑
						grandparent->_col = RED;//祖父变红
					}
					//情况3
					//   g
					//  u  p
					//    c
					//右左双旋+变色
					else {
						rotate_right(parent);
						rotate_left(grandparent);
						//看着图写就行了
						cur->_col = BLACK;
						grandparent->_col = RED;
					}
					break;
				}
			}
		}
		_root->_col = BLACK;//最后无论根是红是黑 -- 都处理成黑
		//这里我们要返回新增的节点 -- 但是刚才插入过程可能不见了,所以前面最好保存一下
		return make_pair(iterator(newnode), true);
	}
};

Supongo que te gusta

Origin blog.csdn.net/Yu_Cblog/article/details/131751629
Recomendado
Clasificación