底层实现set和map

本文详细介绍了红黑树的实现,并通过C++模板技术模拟实现了set和map的数据结构。内容包括红黑树节点结构、迭代器操作以及set和map的插入操作。同时,针对set和map的不同需求,定义了不同的键值提取函数以适应不同场景。通过封装,简化了红黑树在实际应用中的使用。

  • 红黑树的实现blog链接link.

1. 改进红黑树

通过STL可以得到,对于set和map的实现底层所使用的的都是红黑树,所以这里比较难得是对模板的使用。

#pragma once

enum Color
{
	RED,
	BLACK
};

template<class T>
struct RBTreeNode
{
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	T _t;

	enum Color _col;

	RBTreeNode(const T& t)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _t(t)
		, _col(RED)
	{}
};

template<class T, class Ref, class Ptr>
struct RBTreeIterator
{
	typedef RBTreeIterator<T, Ref, Ptr> Self;
	typedef RBTreeNode<T> Node;
	Node* _node;
	RBTreeIterator(Node* node)
		:_node(node)
	{}

	Ref operator*()
	{
		return _node->_t;
	}

	//对于set来说,*it可以直接的取到Key值
	//但是对于map来说,it->first
	Ptr operator->()
	{
		return &(_node->_t);//如果是map那么这里返回的是一个pair<const K,V>*
	}

	// ++it 前置
	//对于这个迭代器的++来说,还是比较需要在重新好好的思考一下的
	Self& operator++()
	{
		//简单点说,当开始访问我这个结点的时候,说明这个结点的左树已经访问完了,此时分情况就要看右树是不是空
		// 找中序的下一个
		if (_node->_right)
		{
			// 右树的最左节点
			Node* cur = _node->_right;
			while (cur->_left)
			{
				cur = cur->_left;
			}

			_node = cur;
		}
		else
		{
			// 右为空,_node所在的子树已经访问完了,
			// 沿着路径往根走,找孩子不是父亲的右的那个祖先
			
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_right == cur)
			{
				cur = parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return *this; //*this返回的是本身
	}

	//对于++来说,左子树 根 右子树
	//对于--来说,右子树 根 左子树,和++的逻辑刚好相反
	Self& operator--()
	{
		//也就是当走到那个结点的时候,右子树和根已经遍历完了,所以此时应该看左子树
		if (_node->_left)
		{
			//应该找左子树中的最右结点,也就是最大的结点
			Node* cur = _node->_left;
			while (cur->_right)
			{
				cur = cur->_right;
			}
			_node = cur;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_left == cur)
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	bool operator!=(const Self& s) const
	{
		return _node != s._node;
	}
};


// set<K>    -> RBTree<K, K>
// map<K, V> -> RBTree<K,pair<const K, V>>
template<class K, class T, class KeyOfT>
struct RBTree
{
public:
	typedef RBTreeIterator<T, T&, T*> Iterator;
	typedef RBTreeIterator<T, const T&, const T*> Const_Iterator;
	typedef RBTreeNode<T> Node;
	//对于红黑树的Begin()迭代器来说,应该从中序遍历的第一个开始
	Iterator Begin()
	{
		Node* cur = _root;
		while (cur && cur->_left)
		{
			cur = cur->_left;
		}
		return Iterator(cur);
	}

	Iterator End()
	{
		return Iterator(nullptr);
	}


	//bool Find(const K& k);
	pair<Iterator, bool> Insert(const T& t)
	{
		KeyOfT kot;

		if (_root == nullptr)
		{
			_root = new Node(t);

			_root->_col = BLACK;
			return make_pair(Iterator(_root), true);
		}

		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (kot(cur->_t) < kot(t))
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kot(cur->_t) > kot(t))
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return make_pair(Iterator(cur), false);
			}
		}

		cur = new Node(t); // RED
		if (kot(parent->_t) < kot(t))
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_left = cur;
			cur->_parent = parent;
		}

		Node* newnode = cur;

		// 
		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			if (grandfather->_left == parent)
			{
				Node* uncle = grandfather->_right;
				// 情况1:u存在且为红
				if (uncle && uncle->_col == RED)
				{
					// 变色
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					// 继续往上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else //  情况2+3:u不存在或存在且为黑
				{
					//        g
					//      p
					//   c
					//
					if (cur == parent->_left)  
					{
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						//        g
						//      p
						//         c
						//
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
			else // grandfather->_right == parent
			{
				Node* uncle = grandfather->_left;
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;

					cur = grandfather;
					parent = cur->_parent;
				}
				else //  情况2+3:u不存在或存在且为黑
				{
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					else
					{
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}

					break;
				}
			}
		}


		_root->_col = BLACK;
		return make_pair(Iterator(newnode), true);
	}


	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

		Node* parentParent = parent->_parent;

		subL->_right = parent;
		parent->_parent = subL;

		if (parent == _root)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			if (parentParent->_left == parent)
			{
				parentParent->_left = subL;
			}
			else
			{
				parentParent->_right = subL;
			}

			subL->_parent = parentParent;
		};
	}

	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		if (subRL)
		{
			subRL->_parent = parent;
		}

		subR->_left = parent;

		Node* parentParent = parent->_parent;
		parent->_parent = subR;

		if (_root == parent)
		{
			_root = subR;
		}
		else
		{
			if (parentParent->_left == parent)
			{
				parentParent->_left = subR;
			}
			else
			{
				parentParent->_right = subR;
			}
		}

		subR->_parent = parentParent;
	}


private:
	Node* _root = nullptr;
};
  1. 红黑树的结点内存的不再是具体的类型Key值或者pair<const K,V>,而是一种更高维度的泛型,通过这个模板参数实现对同一颗树的复用。
  2. 对于模板所传了一个KeyOfT的仿函数的作用就是取出T模型中的K值,如果你是set那么T就是K,但是如果你是map的话,T就是pair<const K,V>,那么这里并不能拿来直接的比较大小,所以还需要将里面具体的Key值取出来,在进行比较大小才可以。
  3. 还有一个就是对于迭代器中的operator++()和operator–()需要好好的理解。对于迭代器的++操作来说,应该走的是中序遍历的过程,比如此时走到了7这个结点,下一个怎么找到,就是问题。

在这里插入图片描述

2. 对于set模拟封装

对于set和map来说,自己本身是没有什么内容的,他们就都是基于红黑树的基础上所实现的,底层所使用的也都是红黑树的结构和接口。

#pragma once
#include "RBTree.h"

namespace wzy
{
	template<class K>
	class set
	{
		struct SetKOfT
		{
			const K& operator()(const K& k)
			{
				return k;
			}
		};
	public:
		//对于set的迭代器其实就是对红黑树的迭代器进行了封装
		//这里typename还有一个作用:此时代码走到这里,模板还没有实例化,所以找不到
		//相当于告诉编译器,别急着报错,先放下,等待实例化之后再来找,如果还找不到,在报错也不急
		typedef typename RBTree<K, K, SetKOfT>::Iterator iterator;

		iterator begin()
		{
			return _t.Begin();
		}

		iterator end()
		{
			return _t.End();
		}


		//pair<RBTreeNode<K>*, bool> insert(const K& k)
		pair<iterator, bool> insert(const K& k)
		{
			return _t.Insert(k);
		}

	private:
		RBTree<K, K, SetKOfT> _t;
	};

	void test_set()
	{
		set<int> s;
		s.insert(1);
		s.insert(20);
		s.insert(12);
		s.insert(2);
		s.insert(23);
		s.insert(21);
		s.insert(2);
		s.insert(30);

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

在这里插入图片描述

3. 对于map模拟封装

#pragma once
#include "RBTree.h"

namespace wzy
{
	template<class K, class V>
	class map
	{
		struct MapKOfT
		{
			const K& operator()(const pair<const K, V>& kv)
			{
				return kv.first;
			}
		};

	public:
		typedef typename RBTree<K, pair<const K, V>, MapKOfT>::Iterator iterator;
		iterator begin()
		{
			return _t.Begin();
		}

		iterator end()
		{
			return _t.End();
		}
		//pair<RBTreeNode<pair<const K,V>>*, bool> insert(const pair<const K, V>& kv)
		pair<iterator, bool> insert(const pair<const K, V>& kv)
		{
			return _t.Insert(kv);
		}

		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = insert(make_pair(key, V()));
			//ret.first拿到迭代器,->拿到了pair,然后返回第二个参数
			return ret.first->second;
		}

	private:
		RBTree<K, pair<const K, V>, MapKOfT> _t;
	};

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


		//
		//map<int, int>::iterator it = m.begin();
		//while (it != m.end())
		//{
		//	cout << it->first << " ";
		//	++it;
		//}
		//cout << endl;


		map<string, string> dict;
		dict["string"] = "字符串";
		dict["sort"] = "排序";

		map<string, string>::iterator it = dict.begin();
		while (it != dict.end())
		{
			cout << it->first << ":" << it->second << endl;
			++it;
		}

		////支持迭代器就支持范围for
		//for (const auto& e : dict)
		//{
		//	cout << e.first << ":" << e.second << endl;
		//}
	}
}

在这里插入图片描述

  1. 对于map来说是重载了[]的,但是底层其实就是调用insert函数帮忙实现的,不管成功不成功,返回的都是Value的引用。
### Java中SetMap底层实现原理及数据结构分析 #### 1. Set底层实现原理及数据结构 `Set` 接口的主要特点是不允许重复元素。其实现类主要包括 `HashSet`, `LinkedHashSet`, `TreeSet`。 - **HashSet**: `HashSet` 是基于 `HashMap` 实现的,它的核心思想是利用 `HashMap` 的 key 来存储集合中的元素[^3]。由于 `HashMap` 不允许相同的 key 存在,因此 `HashSet` 能够自然地保证元素的唯一性。`HashSet` 中的元素是没有顺序的(即无序),因为它们依赖于哈希值决定存储位置。 - **LinkedHashSet**: `LinkedHashSet` 继承自 `HashSet` 并基于 `LinkedHashMap` 实现[^4]。除了保持元素唯一外,还维护了一个双向链表来记录插入顺序,从而使得迭代时能按照插入顺序返回元素。 - **TreeSet**: `TreeSet` 底层使用的是 `TreeMap` 数据结构[^3]。它提供了有序的功能,默认按升序排列元素。如果需要自定义排序,则可以通过提供一个实现了 `Comparator` 接口的对象作为构造器参数[^3]。这种排序机制由红黑树支持,确保了高效的查找性能 O(log n)。 #### 2. Map底层实现原理及数据结构 `Map` 接口中常见的实现有 `HashMap`, `LinkedHashMap`, `TreeMap`。 - **HashMap**: 在 JDK 1.8 及之后版本中,`HashMap` 使用了一种混合的数据结构——数组加链表或者红黑树。初始状态下,`HashMap` 将键值对存放在桶 (bucket) 数组里。当发生碰撞(两个不同对象具有相同哈希值)时,这些对象会被放入同一桶内的链表节点中。一旦某个桶里的链表长度超过了设定阈值(默认为 8),并且当前数组大小达到一定条件时,这个链表就会被转换成一棵红黑树以提高查询效率[^1]。 - **LinkedHashMap**: `LinkedHashMap` 延续了 `HashMap` 的设计思路,但在其中增加了额外的双向链表用来追踪访问或写入顺序[^4]。这使其既可以像普通 `HashMap` 那样快速定位任意条目,又能维持特定次序以便遍历操作更加可预测。 - **TreeMap**: `TreeMap` 利用了红黑树这一平衡二叉搜索树来进行内部管理。这意味着所有的键都必须是可以比较的(要么自己实现 Comparable 接口,要么外部指定 Comparator)。相比起其他 map 类型来说,虽然牺牲了一些速度换取稳定性,但它非常适合那些需要频繁检索范围区间的情况。 ```java // 示例代码展示如何创建并填充 HashMap HashSet import java.util.HashMap; import java.util.HashSet; public class Example { public static void main(String[] args){ // 创建 HashMap 示例 HashMap<String, Integer> map = new HashMap<>(); map.put("Apple", 1); // 创建 HashSet 示例 HashSet<Integer> set = new HashSet<>(); set.add(5); } } ``` #### 性能对比与适用场景 对于大多数应用而言,如果没有特别需求的话通常会选择 `HashMap` 或者 `HashSet` 因为其较高的运行效率。然而当我们关心数据之间的相对关系或者是希望获得某种预设好的序列化形式的时候就可能需要用到 `TreeMap/TreeSet` 或者 `LinkedHashMap/LinkedHashSet`. ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值