【C++】map的模拟实现

前言:

C++中的map是标准模板库(STL)中的一种关联容器,它存储的是键值对(key-value pairs),其中每个键都是唯一的。

map的特点

  • **值唯一性:**map中的每个键只能出现一次,如果尝试插入具有相同键的新元素,则会覆盖原有键对应的值。
  • 自动排序:map中的元素是根据键值自动排序的,默认情况下是按照键的升序排序。
  • 动态调整:插入和删除操作会自动调整内部结构以保持有序性和平衡性。
  • 支持复杂数据类型:map的键和值可以是任意数据类型,包括自定义类型。
  • 迭代器支持:map提供双向迭代器,可以遍历容器中的所有元素。
  • **成员函数丰富:**map提供了一系列成员函数,如insert、erase、find、count、begin、end等,用于执行各种操作。
  • 空间和性能开销:由于使用红黑树,map可能会有较大的空间开销,并且相比于哈希表(如unordered_map),插入和删除操作的时间复杂度为对数级别,可能在某些高性能要求的应用中不如哈希表高效

map的实现 参考:红黑树

map的存储结构

  • 由于set和map是公用一棵树,set是K 、map是KV的结构,为了适应map的结构,set做出一定的改变
  • 内部类是为了取到map内所存储的数据
  • 在set的map中 set所存储的是key而map是pair。这里取到的值是first。
template<class K, class V>
    class map
    {
        struct mapofT
        {
            const K& operator()(const pair<K, V>& kv)
            {
                return kv.first;
            }
        };
        public:
        private:
        RBTree<K, pair<const K, V>, mapofT> _t;
    };

map的插入

插入实现的基本步骤

  • 查找插入位置**:map内部使用红黑树来维护元素的顺序,因此插入操作首先需要在树中找到正确的位置。这通常涉及到比较键值并沿着树进行遍历。

  • **创建节点:**一旦找到了插入位置,就会创建一个新的节点来存储要插入的键值对。

  • **插入节点:**新创建的节点会被插入到红黑树中的正确位置,这可能涉及到树的旋转和重新着色操作,以保持红黑树的平衡性质。

  • **返回插入结果:**insert函数返回一个pair,其中第一个元素是一个迭代器,指向新插入的元素(如果键不存在),或者指向已存在键的元素;第二个元素是一个布尔值,指示插入是否成功(即键是否不存在)。

pair<iterator,bool> insert(const T& data)
{
	if (_root == nullptr)
	{
		_root = new Node(data);
		_root->_col = BLACK;
		return make_pair(iterator(_root),true);
	}
	Node* parent = nullptr;
	Node* cur = _root;

	keyofT kot;

	while (cur)
	{
		if (kot(data) > kot(cur->_data))
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (kot(data) < kot(cur->_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(data) < kot(parent->_data))
	{
		parent->_left = cur;
	}
	else
	{
		parent->_right = cur;
	}
	cur->_parent = parent;

	while (parent && parent->_col == RED)
	{
		Node* grandparent = parent->_parent;
		if (parent == grandparent->_left)
		{
			Node* uncle = grandparent->_right;
			if (uncle && uncle->_col == RED)
			{
				parent->_col = uncle->_col = BLACK;
				grandparent->_col = RED;
				cur = grandparent;
				parent = cur->_parent;
			}
			else//uncle is not or black
			{
				if (cur == parent->_left)
				{
					RotateR(grandparent);
					grandparent->_col = RED;
					parent->_col = BLACK;
				}
				else
				{
					RotateL(parent);
					RotateR(grandparent);
					grandparent->_col = RED;
					cur->_col = BLACK;
				}
				break;
			}
		}
		else//parent == grandparent->_right
		{
			Node* uncle = grandparent->_left;

			if (uncle && uncle->_col == RED)
			{
				grandparent->_col = RED;
				parent->_col = uncle->_col = BLACK;
				cur = grandparent;
				parent = cur->_parent;
			}
			else
			{
				if (cur == parent->_right)
				{
					RotateL(grandparent);
					parent->_col = BLACK;
					grandparent->_col = RED;
				}
				else
				{
					RotateR(parent);
					RotateL(grandparent);
					cur->_col = BLACK;
					grandparent->_col = RED;
				}
			}
			break;
		}
	}
	_root->_col = BLACK;
	return make_pair(iterator(newnode),true );
}

map的查找

  1. 调用find成员函数,传入要查找的键作为参数。
  2. find函数在内部遍历红黑树,使用键值进行比较,寻找匹配的元素。
  3. 如果找到了匹配的键,find返回一个指向该元素的迭代器。
  4. 如果没有找到匹配的键,find返回end()迭代器。
  5. 红黑树的查找类似于,AVL的查找。本质上是一样的。
Node* find(const T& data)
{
    keyofT kot;
    Node* cur = _root;
    while (cur)
    {
        if (kot(data) > kot(cur->_data))
        {
            cur = cur->_right;
        }
        else if (kot(data) < kot(cur->_data))
        {
            cur = cur->_left;
        }
        else
        {
            return cur;
        }
    }

    return nullptr;
}

map重载[]

  • STL中也是调用insert。
  • 传入K返回V的形式。
V& operator[] (const K& key)
{
    pair<iterator, bool> ret = insert(make_pair(key,V()));

    return ret.first->second;
}

map的迭代器

C++中的map是一个关联容器,它存储键值对(key-value pairs),并且根据键(key)来快速检索值(value)。map的迭代器是一种指针,指向map中的元素。map的迭代器有两种类型:const_iterator和iterator。const_iterator用于只读访问,而iterator可以读写。

迭代器的特性

  • map的迭代器遵循双向迭代器的要求,可以向前和向后遍历。

  • 迭代器在map被修改时(例如插入或删除操作)可能会失效。

  • map的迭代器可以通过解引用操作符(*)来访问指向的元素,或者使用箭头操作符(->)来访问元素的成员。

  • set迭代器类似于list的迭代器

迭代器的封装

  • 主要区别在于++--
  • Ptr是T*,Ref是T&,设置这么多的模板参数是为适配出const的迭代器
template<class T,class Ptr,class Ref>
struct TreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef TreeIterator<T,Ptr,Ref> self;
	typedef TreeIterator<T, T*, T&> Iterator;
	Node* _node;

	TreeIterator(Node* node)
		:_node(node)
	{}
	TreeIterator(const Iterator& it)
		:_node(it._node)
	{}

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

	Ptr operator->()
	{
		return &_node->_data;
	}
	
	bool operator != (const self & s) const 
	{
		return _node!= s._node;
	}
	bool operator== (const self & s) const 
	{
		return _node == s._node;
	}

};

前置++

  • 前置++ 的本质上就是中序的遍历,左子树-根-右子树
  • 如果右子树存在就去右子树的最左节点
  • _node 不是右节点那么,证明子树的左右节点均访问完成,需要访问祖父的节点
self& operator++()
{
    if (_node->_right)
    {
        Node* cur = _node->_right;
        while (cur->_left)
        {
            cur = cur->_left;
        }
        _node = cur;
    }
    else
    {
        Node* cur = _node;
        Node* parent = _node->_parent;
        while (parent)
        {
            if (cur == parent->_left)
            {
                break;
            }
            else
            {
                cur = cur->_parent;
                parent = parent->_parent;
            }

        }

        _node = parent;
    }
    return *this;
}

前置–

  • 前置-- 是前置++的镜像的一个顺序的访问,右子树-根-左子树
  • 方法是类似于前置++
self& operator--()
{
	if (_node->_left)
	{
		Node* cur = _node->_right;
		while (cur->_left)
		{
			cur = cur->_right;
		}
		_node = cur;
	}
	else
	{
		Node* cur = _node;
		Node* parent = _node->_parent;
		while (parent && parent->_left)
		{
			cur = cur->_parent;
			parent = parent->_parent;
		}

		_node = parent;
	}
	return *this;
}

迭代器

C++中的map是一种关联容器,它存储键值对(key-value pairs),并且按照键的顺序自动排序。map的迭代器用于遍历容器中的元素。map提供了多种迭代器成员函数,包括:

  • **begin():**返回指向map中第一个元素的迭代器。

  • **end():**返回指向map容器末尾位置的迭代器,这个位置不包含任何元素,用作遍历结束的标志。

  • **rbegin():**返回指向map最后一个元素的反向迭代器。

  • **rend():**返回指向map第一个元素的反向迭代器。

  • map的迭代器是const_iterator类型的,这意味着它们提供对元素的只读访问。如果需要修改元素,可以使用iterator类型

  • map的迭代器提供了向前和向后遍历的能力,但不支持随机访问迭代器的所有操作,如算术运算

  • 前面是将迭代器封装,因为正常的++或者–已不可以支持自定义类型的加减

  • 在红黑树的类中实现,在实现set时候只需要调用,在这里面可以认为红黑树是容器的适配器

typedef TreeIterator<T, T*, T&> iterator;

typedef TreeIterator<T,const T*,const T&> const_iterator;

iterator begin()
{
    Node* leftmin = _root;
    while (leftmin->_left)
    {
        leftmin = leftmin->_left;
    }
    return iterator(leftmin);
}

iterator end()
{
    return iterator(nullptr);
}

const_iterator begin()const 
{
    Node* leftmin = _root;
    while (leftmin->_left)
    {
        leftmin = leftmin->_left;
    }
    return const_iterator(leftmin);
}

const_iterator end()const 
{
    return const_iterator(nullptr);
}

源码

map

#pragma once

#include"RBTree.h"
#include<iostream>
using namespace std;

namespace Map
{
	template<class K, class V>
	class map
	{
		struct mapofT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:


		typedef typename RBTree<K, pair<const K, V>, mapofT>::iterator iterator;
		typedef typename RBTree<K, pair<const K, V>, mapofT>::const_iterator const_iterator;

		V& operator[] (const K& key)
		{
			pair<iterator, bool> ret = insert(make_pair(key,V()));
													
			return ret.first->second;
		}

		pair<iterator,bool> insert(const pair<K,V>& kv)
		{
			return _t.insert(kv);
		}
		iterator begin()
		{
			return _t.begin();
		}

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

		const_iterator begin()const
		{
			return _t.begin();
		}

		const_iterator end()const
		{
			return _t.end();
		}
	private:
		RBTree<K, pair<const K, V>, mapofT> _t;
	};
}

红黑树

#pragma once

#include<iostream>
#include<assert.h>
#include<utility>

using namespace std;

	enum Colour
	{
		RED,
		BLACK
	};
	template< class T>
	class RBTreeNode
	{
	public:
		RBTreeNode<T>* _left;
		RBTreeNode<T>* _right;
		RBTreeNode<T>* _parent;
		T _data;
		Colour _col;

		RBTreeNode(const T& data)
			:_left(nullptr), _right(nullptr), _parent(nullptr), _col(RED), _data(data)
		{}
	};
	 
	template<class T,class Ptr,class Ref>
	struct TreeIterator
	{
		typedef RBTreeNode<T> Node;
		typedef TreeIterator<T,Ptr,Ref> self;
		typedef TreeIterator<T, T*, T&> Iterator;
		Node* _node;

		TreeIterator(Node* node)
			:_node(node)
		{}
		TreeIterator(const Iterator& it)
			:_node(it._node)
		{}

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

		Ptr operator->()
		{
			return &_node->_data;
		}
		
		bool operator != (const self & s) const 
		{
			return _node!= s._node;
		}
		bool operator== (const self & s) const 
		{
			return _node == s._node;
		}
		self& operator--()
		{
			if (_node->_left)
			{
				Node* cur = _node->_right;
				while (cur->_left)
				{
					cur = cur->_right;
				}
				_node = cur;
			}
			else
			{
				Node* cur = _node;
				Node* parent = _node->_parent;
				while (parent && parent->_left)
				{
					cur = cur->_parent;
					parent = parent->_parent;
				}

				_node = parent;
			}
			return *this;
		}
		
		self& operator++()
		{
			if (_node->_right)
			{
				Node* cur = _node->_right;
				while (cur->_left)
				{
					cur = cur->_left;
				}
				_node = cur;
			}
			else
			{
				Node* cur = _node;
				Node* parent = _node->_parent;
				while (parent)
				{
					if (cur == parent->_left)
					{
						break;
					}
					else
					{
						cur = cur->_parent;
						parent = parent->_parent;
					}

				}

				_node = parent;
			}
			return *this;
		}
	};

	template<class K, class T,class keyofT>
	class RBTree
	{
		typedef RBTreeNode<T> Node;

	public:
		typedef TreeIterator<T, T*, T&> iterator;

		typedef TreeIterator<T,const T*,const T&> const_iterator;

		iterator begin()
		{
			Node* leftmin = _root;
			while (leftmin->_left)
			{
				leftmin = leftmin->_left;
			}
			return iterator(leftmin);
		}

		iterator end()
		{
			return iterator(nullptr);
		}

		const_iterator begin()const 
		{
			Node* leftmin = _root;
			while (leftmin->_left)
			{
				leftmin = leftmin->_left;
			}
			return const_iterator(leftmin);
		}

		const_iterator end()const 
		{
			return const_iterator(nullptr);
		}


		Node* find(const T& data)
		{
			keyofT kot;
			Node* cur = _root;
			while (cur)
			{
				if (kot(data) > kot(cur->_data))
				{
					cur = cur->_right;
				}
				else if (kot(data) < kot(cur->_data))
				{
					cur = cur->_left;
				}
				else
				{
					return cur;
				}
			}

			return nullptr;
		}

		pair<iterator,bool> insert(const T& data)
		{
			if (_root == nullptr)
			{
				_root = new Node(data);
				_root->_col = BLACK;
				return make_pair(iterator(_root),true);
			}
			Node* parent = nullptr;
			Node* cur = _root;

			keyofT kot;

			while (cur)
			{
				if (kot(data) > kot(cur->_data))
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (kot(data) < kot(cur->_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(data) < kot(parent->_data))
			{
				parent->_left = cur;
			}
			else
			{
				parent->_right = cur;
			}
			cur->_parent = parent;

			while (parent && parent->_col == RED)
			{
				Node* grandparent = parent->_parent;
				if (parent == grandparent->_left)
				{
					Node* uncle = grandparent->_right;
					if (uncle && uncle->_col == RED)
					{
						parent->_col = uncle->_col = BLACK;
						grandparent->_col = RED;
						cur = grandparent;
						parent = cur->_parent;
					}
					else//uncle is not or black
					{
						if (cur == parent->_left)
						{
							RotateR(grandparent);
							grandparent->_col = RED;
							parent->_col = BLACK;
						}
						else
						{
							RotateL(parent);
							RotateR(grandparent);
							grandparent->_col = RED;
							cur->_col = BLACK;
						}
						break;
					}
				}
				else//parent == grandparent->_right
				{
					Node* uncle = grandparent->_left;

					if (uncle && uncle->_col == RED)
					{
						grandparent->_col = RED;
						parent->_col = uncle->_col = BLACK;
						cur = grandparent;
						parent = cur->_parent;
					}
					else
					{
						if (cur == parent->_right)
						{
							RotateL(grandparent);
							parent->_col = BLACK;
							grandparent->_col = RED;
						}
						else
						{
							RotateR(parent);
							RotateL(grandparent);
							cur->_col = BLACK;
							grandparent->_col = RED;
						}
					}
					break;
				}
			}
			_root->_col = BLACK;
			return make_pair(iterator(newnode),true );
		}

		void RotateR(Node* parent)
		{
			Node* cur = parent->_left;
			Node* curright = cur->_right;

			parent->_left = curright;

			if (curright)
			{
				curright->_parent = parent;
			}

			cur->_right = parent;

			Node* ppnode = parent->_parent;

			parent->_parent = cur;

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

				cur->_parent = ppnode;
			}

		}
		void RotateL(Node* parent)
		{

			Node* cur = parent->_right;

			Node* curleft = cur->_left;

			parent->_right = curleft;

			if (curleft)
			{
				curleft->_parent = parent;
			}

			cur->_left = parent;

			Node* ppnode = parent->_parent;

			parent->_parent = cur;

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

				cur->_parent = ppnode;
			}
		}


		bool is_balance()
		{
			return is_balance(_root);
		}

		bool Checknum(Node* root, int blacknum, int stdnum)
		{
			if (root == nullptr)
			{
				if (blacknum != stdnum)
				{
					return false;
				}
				return true;
			}

			if (root->_col == BLACK)
			{
				++blacknum;
			}

			return Checknum(root->_left, blacknum, stdnum)
				&& Checknum(root->_right, blacknum, stdnum);
		}
		bool is_balance(Node* root)
		{
			if (root == nullptr)
			{
				return true;
			}

			if (root->_col == RED)
			{
				return false;
			}
			int stdnum = 0;
			Node* cur = root;
			while (cur)
			{
				if (cur->_col == BLACK)
				{
					++stdnum;
				}
				cur = cur->_left;
			}

			return Checknum(root, 0, stdnum);
		}

	private:
		Node* _root = nullptr;
	};

  • 36
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 20
    评论
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值