Simulation implementation of map and set|Using red-black tree to encapsulate map and set|STL source code analysis

foreword

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

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

高质量干货博客汇总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 These two are columns for bloggers to learn data structures and simulate various containers in the STL standard template library.

STL source code analysis https://blog.csdn.net/yu_cblog/category_11983210.html?spm=1001.2014.3001.5482 hand-torn data structure https://blog.csdn.net/yu_cblog/category_11490888.html


What is the bottom layer of Map and Set

In C++ STL, mapand setthe bottom layer is usually implemented using a red-black tree (Red-Black Tree).

A red-black tree is a self-balancing binary search tree that maintains balance by storing additional information (color) at each node. These color messages follow a specific set of rules to ensure that the tree remains in a balanced state. The balanced nature of red-black trees makes insertion, deletion, and lookup operations logarithmic in time complexity.

In a red-black tree, each node has a key-value pair, sorted in the order of the keys. For map, each node also has an associated value, and for set, a node's key is its value. This ordered nature makes mapand setvery suitable for application scenarios that need to be searched, inserted and deleted by key.

It should be noted that although the red-black tree is a common underlying implementation of mapand set, the specific implementation may vary with different compilers and standard library implementations. Therefore, the underlying implementation may vary for specific compiler and standard library versions.

Therefore, before learning how to use the red-black tree to encapsulate the map and set of STL, we need to learn the underlying principle of the red-black tree. You can see the blogger’s previous blog, which talks about the principle of the red-black tree very well. detailed.

Hand torn red-black tree | Color change + rotation Do you really understand? [Super-intentional and super-detailed graphic explanation | An article to learn Red_Black_Tree] icon-default.png?t=N6B9https://blog.csdn.net/Yu_Cblog/article/details/128210260

However, the red-black tree in the above blog is slightly different from the version to be packaged.

Because set only needs key, and map is a key-value structure.

The blogger of the packaged version of the red-black tree header file is placed at the end of the blog.

encapsulation

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

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

RBTree.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);
	}
};

Guess you like

Origin blog.csdn.net/Yu_Cblog/article/details/131751629