[数据结构]-map和set

这篇具有很好参考价值的文章主要介绍了[数据结构]-map和set。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

[数据结构]-map和set,数据结构,数据结构作者小蜗牛向前冲

[数据结构]-map和set,数据结构,数据结构名言我可以接受失败,但我不能接受放弃

  如果觉的博主的文章还不错的话,还请[数据结构]-map和set,数据结构,数据结构点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 

目录

一、键值对

二、set

1、set的基本知识

2、set的使用 

三、map

1、map的基本知识

2、map的使用 

3、multiset和multimap

4、oj的运用

四、map和set的模拟实现 

1、红黑树迭代器

2、set.h模拟实现 

3、map.h模拟实现


本期学习目标:理解什么是键值对,实现红黑树的迭代器,模拟实现map和set. 

一、键值对

键值对是一种简单但强大的数据表示方式,通常用于构建关联关系。它由两部分组成:键(Key)和值(Value)。每个键都唯一地标识一个值。这种数据结构被广泛用于编程中的各种场景

举例来说,考虑一个电话簿,其中每个人的名字(键)都对应着他们的电话号码(值)。在这个例子中,名字就是键,电话号码就是值。这样的组织方式使得我们可以通过名字快速查找到对应的电话号码。

SGI-STL中关于键值对的定义:

template <class T1, class T2>
struct pair
{
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair(): first(T1()), second(T2())
{}
pair(const T1& a, const T2& b): first(a), second(b)
{}
}

在map和set我们的都有键值对的运用,具体运用场景下面会一一道来,这里我们知要明白键值对有二个按键,都能唯一 标识一个值。

二、set

1、set的基本知识

  • 1. set是按照一定次序存储元素的容器
  • 2. 在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。 set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。
  • 3. 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序
  • 4. set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对 子集进行直接迭代。
  • 5. set在底层是用二叉搜索树(红黑树)实现的

[数据结构]-map和set,数据结构,数据结构

  •  T: set中存放元素的类型,实际在底层存储的键值对。
  • Compare:set中元素默认按照小于来比较 Alloc:set中元素空间的管理方式,使用STL提供的空间配置器管理

注意:

  • set中只放 value,但在底层实际存放的是由构成的键值对。
  • set中插入元素时,只需要插入value即可,不需要构造键值对。
  • set中的元素不可以重复(因此可以使用set进行去重)。
  • 使用set的迭代器遍历set中的元素,可以得到有序序列。
  • set中的元素默认按照小于来比较
  • set中查找某个元素,时间复杂度为:log_2 n

2、set的使用 

set的构造

函数声明 功能介绍
set (const Compare& comp = Compare(), const Allocator& = Allocator() ); 构造空的set
set (InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& = Allocator() ); 用[first, last)区 间中的元素构造 set
set ( const set<key,compare,Allocator>& x ); set的拷贝构造

set的迭代器

[数据结构]-map和set,数据结构,数据结构

set的容量 

[数据结构]-map和set,数据结构,数据结构set修改操作

[数据结构]-map和set,数据结构,数据结构

这些接口和前面的设计都非常类似,这里就不在一一分析了。

 下面我们快速使用上面的接口,了解一下set

void test1()
{
	set<int> s;
	s.insert(4);
	s.insert(67);
	s.insert(2);
	s.insert(1);
	s.insert(55);
	s.insert(11);
	s.insert(5);

	for (auto v : s)
	{
		cout << v << " " ;
		v++;
	}
	cout << endl;
	auto it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;

	/*auto pos = s.find(55);*/
	auto pos = find(s.begin(), s.end(), 55);
	if (pos != s.end())
	{
		s.erase(pos);
	}
	cout << s.erase(67) << endl;
	cout << s.erase(11) << endl;

	it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;

	//s.count的功能和find类似
}

[数据结构]-map和set,数据结构,数据结构

三、map

1、map的基本知识

[数据结构]-map和set,数据结构,数据结构

  • 1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的 素。
  • 2. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型 value_type绑定在一起,为其取别名称为pair: typedef pair value_type;
  • 3. 在内部,map中的元素总是按照键值key进行比较排序的
  • 4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序 对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
  • 5. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value
  • 6. map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。

 注意:

1. map中的的元素是键值对

2. map中的key是唯一的,并且不能修改

3. 默认按照小于的方式对key进行比较

4. map中的元素如果用迭代器去遍历,可以得到一个有序的序列

5. map的底层为平衡搜索树(红黑树),查找效率比较高O(log_2 N)

6. 支持[]操作符,operator[]中实际进行插入查找 

2、map的使用 

map的构造

函数声明 功能介绍
map() 构造一个空的map

map的迭代器 

函数声明 功能介绍
begin()和end() begin:首元素的位置,end最后一个元素的下一个位置
cbegin()和cend() 与begin和end意义相同,但cbegin和cend所指向的元素不 能修改
rbegin()和rend() 反向迭代器,rbegin在end位置,rend在begin位置,其 ++和--操作与begin和end操作移动相反
crbegin()和crend() 与rbegin和rend位置相同,操作相同,但crbegin和crend所 指向的元素不能修改

 map的容量与元素访问

函数声明 功能介绍
bool empty ( ) const 检测map中的元素是否为空,是返回 true,否则返回fals
size_type size() const 返回map中有效元素的个数
mapped_type& operator[] (const key_type& k) 返回去key对应的value

这里我们要特别的注意: 

重载的[]不仅仅能够插入和修改元素还能查找元素

[数据结构]-map和set,数据结构,数据结构

map中元素的修改 

[数据结构]-map和set,数据结构,数据结构

快速上手map 

void test1()
{
	map<string,string> dict;
	dict.insert(pair<string, string>("右", "right"));
	dict.insert(pair<string, string>("传说", "legend"));
	dict.insert(make_pair("字符串", "string"));
	dict["迭代器"] = "iterator";

	for (auto kv : dict)
	{
		cout << kv.first << ": " << kv.second << endl;
	}

	string arr[] = { "苹果", "西瓜", "香蕉", "草莓", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
	map<string, int> countMap;
	for (auto& e : arr)
	{
		auto it = countMap.find(e);
		if (it == countMap.end())
		{
			// 元素不存在,插入它并初始化计数为 1
			countMap.insert(make_pair(e, 1));
		}
		else
		{
			//元素以及存在递增
			it->second++;
		}
	}

	for (const auto& kv : countMap)
	{
		cout << kv.first << " " << kv.second << endl;
	}
}

[数据结构]-map和set,数据结构,数据结构

这里我们用map就完美的实现了kv模型 

这里我们特别注意map的插入和以前学习的数据结构不一样,不在是仅仅直接插入数据,这里插入的是一个pair<类型,类型>("内容1","内容2")

3、multiset和multimap

这二个容器的用法和前面一样,与set和map的区别是set和map里面的值都是不可重复的,而multiset和multimp里面是可以存放相同的值

4、oj的运用

为了加深对map和set的运用,为大家分享了二道oj题

 题1:

[数据结构]-map和set,数据结构,数据结构代码实现:

class Solution {
public:
    struct compare
    {
        bool operator()(const pair<int, string>& l, const pair<int, string>& r)
        {
            return l.first > r.first;
        }
    };

    vector<string> topKFrequent(vector<string>& words, int k) {
        map<string, int> countMap;
        for (auto& str : words)
        {
            countMap[str]++;
        }

        vector<pair<int, string>> v;

        //将map去重后的元素入v
        for (auto& kv : countMap)
        {
            v.push_back(make_pair(kv.second, kv.first));
        }
        //排序
        stable_sort(v.begin(), v.end(), compare());

        vector<string> vv;

        for (int i = 0; i < k; i++)
        {
            vv.push_back(v[i].second);
        }
        return vv;
    }
};

题2: 

[数据结构]-map和set,数据结构,数据结构

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        //用set排序+去重
        set<int> s1(nums1.begin(),nums1.end());
        set<int> s2(nums2.begin(),nums2.end());

        auto it1 = s1.begin();
        auto it2 = s2.begin();

        vector<int> v;
        while(it1 != s1.end() && it2 != s2.end())
        {
            if(*it1 == *it2)
            {
                v.push_back(*it1);
                it1++;
                it2++;
            }
            else if(*it1 < *it2)
            {
                it1++;
            }
            else
            {
                it2++;
            }
        }
        
        return v;
    }
};

四、map和set的模拟实现 

上面我们说了map和set的底层实现是红黑树,前面文章也模拟实现了红黑树,但是为了更加契合map和set的功能,我们还需要对红黑树进行改造。

1、红黑树迭代器

红黑树的迭代器基本框架:

template<class T,class Ref,class Ptr>
struct _RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef _RBTreeIterator<T,Ref,Ptr> Self;
	typedef _RBTreeIterator<T, T&, T*> iterator;
	Node* _node;
	
};

这里大家可能会有疑惑的是为什么要重命名二个模板类型不一样的_RBTreeIterator,self 是表示迭代器自身的类型,而 iterator 是公开接口的迭代器类型。这样由利用不同编程场景的适应

*(解引用)和->(成员访问运算符)

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

对于 *我们应该返回的是当前节点中的数据,对于->返回的是存放当前节点数据的地址。

operator++()和 operator--()

对于红黑树的++操作,就是指向比当前节点更大的树,但是对于一课红黑树来说是存在二种情况的

  • 如果右子树存在,就找右子树的最小
  • [数据结构]-map和set,数据结构,数据结构
  • 如果右子树不存在,
  • 情况1: 如果如果当前节点是其父节点的右子树,或者当前节点是树的根节点,其某个祖先节点。
  • [数据结构]-map和set,数据结构,数据结构
  • 情况2:如果当前节点是其父节点的左子树,那下一个节点就是其父节点,
  • [数据结构]-map和set,数据结构,数据结构

代码实现:

Self& operator++()
{
	//如果右子树存在,就找右子树的最小
	if (_node->_right)
	{
		Node* min = _node->_right;
		while (min->_left)
		{
			min = min->_left;
		}
		//找到了右树的最小
		_node = min;
	}
	else
	{
		Node* cur = _node;
		Node* parent = cur->_parent;
		//找到一个节点是其父节点的左孩子,或者到达根节点
		//如果当前节点是其父节点的左子树,那下一个节点就是其父节点
		//如果如果当前节点是其父节点的右子树,或者当前节点是树的根节点,其某个祖先节点
		while (parent && cur == parent->_right)
		{
			cur = cur->_parent;
			parent = parent->_parent;
		}
		_node = parent;
	}
	return *this;
}

对于红黑树的--操作:情行可以对比++操作的分类完成

	Self& operator--()
	{
		//左子树存在
		if (_node->_left)
		{
			//找左子树中最大
			Node* max = _node->_left;
			while (max->_right)
			{
				max = max->_right;
			}
			_node = max;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			//cur在parent的左
			while (parent && cur == cur->left)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
	}

其他细节的完善,逻辑都比较简单,可以参考下面代码自行完成:

//红黑树的迭代器
template<class T,class Ref,class Ptr>
struct _RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef _RBTreeIterator<T,Ref,Ptr> Self;
	typedef _RBTreeIterator<T, T&, T*> iterator;
	Node* _node;
	
	//构造函数
	_RBTreeIterator(Node* node)
		:_node(node)
	{}

	// const迭代器的时候,他是构造,支持用普通迭代器构造const迭代器
	_RBTreeIterator(const iterator& s)
		:_node(s._node)
	{}
	Ref operator*()
	{
		return  _node->_data;
	}

	Ptr operator->()
	{
		return &(_node->_data);
	}

	Self& operator++()
	{
		//如果右子树存在,就找右子树的最小
		if (_node->_right)
		{
			Node* min = _node->_right;
			while (min->_left)
			{
				min = min->_left;
			}
			//找到了右树的最小
			_node = min;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			//找到一个节点是其父节点的左孩子,或者到达根节点
			//如果当前节点是其父节点的左子树,那下一个节点就是其父节点
			//如果如果当前节点是其父节点的右子树,或者当前节点是树的根节点,其某个祖先节点
			while (parent && cur == parent->_right)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	Self& operator--()
	{
		//左子树存在
		if (_node->_left)
		{
			//找左子树中最大
			Node* max = _node->_left;
			while (max->_right)
			{
				max = max->_right;
			}
			_node = max;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			//cur在parent的左
			while (parent && cur == cur->left)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
	}
	bool operator!=(const Self&s)const
	{
		return _node != s._node;
	}

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

对于之前写的红黑树,我们还做一些变更比如insert的返回值不是简单判断是否插入成功,而是返回一个键值对,返回是当前插入节点的迭代器,并判断是否插入成功。

红黑树完整实现:

#pragma once

enum Colour
{
	RED,
	BLACK,
};

template<class T>
struct RBTreeNode
{
	T _data;
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	Colour _col;

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

//红黑树的迭代器
template<class T,class Ref,class Ptr>
struct _RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef _RBTreeIterator<T,Ref,Ptr> Self;
	typedef _RBTreeIterator<T, T&, T*> iterator;
	Node* _node;
	
	//构造函数
	_RBTreeIterator(Node* node)
		:_node(node)
	{}

	// const迭代器的时候,他是构造,支持用普通迭代器构造const迭代器
	_RBTreeIterator(const iterator& s)
		:_node(s._node)
	{}
	Ref operator*()
	{
		return  _node->_data;
	}

	Ptr operator->()
	{
		return &(_node->_data);
	}

	Self& operator++()
	{
		//如果右子树存在,就找右子树的最小
		if (_node->_right)
		{
			Node* min = _node->_right;
			while (min->_left)
			{
				min = min->_left;
			}
			//找到了右树的最小
			_node = min;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			//找到一个节点是其父节点的左孩子,或者到达根节点
			//如果当前节点是其父节点的左子树,那下一个节点就是其父节点
			//如果如果当前节点是其父节点的右子树,或者当前节点是树的根节点,其某个祖先节点
			while (parent && cur == parent->_right)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	Self& operator--()
	{
		//左子树存在
		if (_node->_left)
		{
			//找左子树中最大
			Node* max = _node->_left;
			while (max->_right)
			{
				max = max->_right;
			}
			_node = max;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			//cur在parent的左
			while (parent && cur == cur->left)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
	}
	bool operator!=(const Self&s)const
	{
		return _node != s._node;
	}

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


// map->RBTree<K, pair<const K, V>, MapKeyOfT> _t;
// set->RBTree<K, K, SetKeyOfT> _t;
template<class K, class T, class KeyOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;
public:
	typedef  _RBTreeIterator<T,T&,T*> iterator;
	typedef  _RBTreeIterator<T, const T&,const T*> const_iterator;


	iterator begin()
	{
		Node* left = _root;
		while (left && left->_left)
		{
			left = left->_left;
		}

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

		return iterator(left);
	}

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


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

	pair<iterator,bool> Insert(const T& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return make_pair(iterator(_root),true);//返回根位置的迭代器,并且插入成功
		}

		KeyOfT kot;
		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;
			cur->_parent = parent;
		}
		else
		{
			parent->_left = cur;
			cur->_parent = parent;
		}

		while (parent && parent->_col == RED)
		{
			Node* grandfater = parent->_parent;
			if (parent == grandfater->_left)
			{
				Node* uncle = grandfater->_right;
				// 情况一  uncle存在且为红
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfater->_col = RED;

					cur = grandfater;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_left)
					{
						// 情况二
						RotateR(grandfater);
						parent->_col = BLACK;
						grandfater->_col = RED;
					}
					else
					{
						// 情况三
						RotateL(parent);
						RotateR(grandfater);
						cur->_col = BLACK;
						grandfater->_col = RED;
					}

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

					cur = grandfater;
					parent = cur->_parent;
				}
				else
				{
					//   g                
					//      p
					//         c
					if (cur == parent->_right)
					{
						RotateL(grandfater);
						parent->_col = BLACK;
						grandfater->_col = RED;
					}
					else
					{
						//   g                
						//      p
						//   c
						RotateR(parent);
						RotateL(grandfater);
						cur->_col = BLACK;
						grandfater->_col = RED;
					}

					break;
				}
			}
		}

		_root->_col = BLACK;

		return make_pair(iterator(newnode),true);
	}

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

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

		Node* ppNode = parent->_parent;
		subR->_left = parent;
		parent->_parent = subR;


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

			subR->_parent = ppNode;
		}
	}

	void RotateR(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)
		if (ppNode == nullptr)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subL;
			}
			else
			{
				ppNode->_right = subL;
			}

			subL->_parent = ppNode;
		}
	}

	void Inorder()
	{
		_Inorder(_root);
	}

	void _Inorder(Node* root)
	{
		if (root == nullptr)
			return;

		_Inorder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_Inorder(root->_right);
	}

	bool Check(Node* root, int blackNum, const int ref)
	{
		if (root == nullptr)
		{
			//cout << blackNum << endl;
			if (blackNum != ref)
			{
				cout << "违反规则:本条路径的黑色节点的数量跟最左路径不相等" << endl;
				return false;
			}

			return true;
		}

		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << "违反规则:出现连续红色节点" << endl;
			return false;
		}

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

		return Check(root->_left, blackNum, ref)
			&& Check(root->_right, blackNum, ref);
	}

	bool IsBalance()
	{
		if (_root == nullptr)
		{
			return true;
		}

		if (_root->_col != BLACK)
		{
			return false;
		}

		int ref = 0;
		Node* left = _root;
		while (left)
		{
			if (left->_col == BLACK)
			{
				++ref;
			}

			left = left->_left;
		}

		return Check(_root, 0, ref);
	}

private:
	Node* _root = nullptr;
};

2、set.h模拟实现 

 文章来源地址https://www.toymoban.com/news/detail-764611.html

#pragma once

#include"RBTree.h"

namespace pjb
{
	template<class K>
	class set
	{
		struct setKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

	public:
		//在C++中,typename 关键字通常用于表示一个依赖于模板参数的类型。在模板中,
		// 有时候编译器无法确定某个名字到底是一个类型还是一个值,这时候就需要使用 typename 
		// 来明确告诉编译器某个名字是一个类型。
		typedef typename RBTree<K, K, setKeyOfT>::iterator iterator;
		typedef typename RBTree<K, K, setKeyOfT>::iterator const_iterator;

		iterator begin()
		{
			return _t.begin();
		}
		iterator end()
		{
			return _t.end();
		}
		const_iterator begin()const
		{
			return _t.begin();
		}
		const_iterator end()const
		{
			return _t.end();
		}
		pair<iterator,bool> insert(const K& key)
		{
			pair<typename RBTree<K, K, setKeyOfT>::iterator, bool> ret = _t.Insert(key);
			return pair<iterator, bool>(ret.first, ret.second);
			/*return _t.Insert(key);*/
		}
	private:
		RBTree<K, K, setKeyOfT> _t;
	};

	void test_set()
	{
		int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
		set<int> s;
		for (auto e : a)
		{
			s.insert(e);
		}

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

		for (auto e : s)
		{
			cout << e << " ";
		}
		cout << endl;
	}
}

测试:

[数据结构]-map和set,数据结构,数据结构

3、map.h模拟实现

#pragma once
#include"RBTree.h"

namespace pjb
{
	template<class K,class V>
	class map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<const K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;
		iterator begin()
		{
			return _t.begin();
		}
		const_iterator begin()const
		{
			return _t.begin();
		}

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

		const_iterator end()const
		{
			return _t.end();
		}
		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()));
			return ret.first->second;
		}
	private:
		RBTree<K, pair<const K, V>, MapKeyOfT> _t;
	};

	void test_map()
	{
		int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
		map<int, int> m;
		for (auto e : a)
		{
			m.insert(make_pair(e, e));
		}

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

		map<string, int> countMap;
		string arr[] = {"西游记","红楼梦","水浒传","三国演义","三国演义" ,"三国演义","水浒传" };
		for (auto& e : arr)
		{
			countMap[e]++;
		}

		for (auto& kv : countMap)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}

}

测试:[数据结构]-map和set,数据结构,数据结构

 

到了这里,关于[数据结构]-map和set的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构,Map和Set的使用方法

    在数据结构中我们经常会使用到 Map 和 Set ,Map 和 Set 到底是什么,它怎样去使用呢?因此博主整理出 Map 和 Set 这两个接口的介绍与使用方法。 目录 1. 啥是Map和Set? 1.1 Map和Set的模型 2. Map的使用 2.1Map的说明 2.2 Java中Map常用的方法 3. Set的使用 3.1Java中Set的常用方法  Map 和 Set

    2023年04月26日
    浏览(41)
  • 【数据结构】 | java中 map和set 详解

    🎗️ 博客新人,希望大家一起加油进步 🎗️ 乾坤未定,你我皆黑马 我们首先来看一下集合的框架结构: Set实现了Collection接口,Map是一个单独存在的接口。 而下面又分别各有两个类,分别是TreeSet(HashSet)和 HashSet(HashMap) Map和Set的作用是用来查找和搜索的;以后涉及到

    2023年04月10日
    浏览(40)
  • 【1++的数据结构】之map与set(一)

    👍作者主页:进击的1++ 🤩 专栏链接:【1++的数据结构】 像list vector dequeue等这样的容器我们称为序列式容器,原因是由于其底层是线性的数据结构,存储的是元素本身。 关联式容器 与序列式容器的区别在于:关联式容器中存储的是键值对,其数据检索时效率更高。 那么什

    2024年02月11日
    浏览(45)
  • Map、Set和哈希表(数据结构系列14)

    目录 前言: 1.搜索树 1.1概念 1.2插入 1.3查找 1.4删除 1.5二叉搜索树整体代码展示  2. Map和Set的讲解 2.1 Map的说明 2.1.1Map的方法 2.2 Set 的说明 2.2.1Set的方法 3.哈希表 3.1哈希表的概念 3.2哈希冲突 3.3冲突的避免 3.4哈希冲突的解决 3.4.1闭散列 3.4.2开散列 结束语: 这节中小编主要与

    2024年02月07日
    浏览(37)
  • 数据结构 - 7(Map和Set 15000字详解)

    二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树: 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值 它的左右子树也分别为二叉搜索树 int[] array ={5,3,4,1,7,8,2,6,0

    2024年02月06日
    浏览(37)
  • Map,List,Set 等集合以及底层数据结构

    集合类存放于java.util包中。集合类存放的都是对象的引用,而非对象本身。常见的集合主要有三种——Set(集)、List(列表)和Map(映射)。其中,List和Set 都 实现 了 Collection 接口,并且List和Set也是接口,而 Map 为独立接口 。常见的实现类如下: List 的实现类有:ArrayList、

    2024年02月09日
    浏览(46)
  • 【数据结构】二叉搜索树与Map和Set

    目录 ♫二叉搜索树 ♪什么是二叉搜索树 ♪二叉搜索树的特性 ♪模拟实现二叉搜索树 ♫Map ♪什么是Map ♪Map的内部类 ♪Map的常用方法 ♪Map的遍历 ♫Set  ♪什么是Set ♪Set的常用方法 ♪Set的遍历 ♪什么是二叉搜索树 二叉搜索树又称二叉排序树,是一种特殊的二叉树,这颗树

    2024年02月07日
    浏览(41)
  • 数据结构之Map/Set讲解+硬核源码剖析

     💕\\\"活着是为了活着本身而活着\\\"💕 作者:Mylvzi    文章主要内容:数据结构之Map/Set讲解+硬核源码剖析    二叉搜索树又叫二叉排序树,他或者是一颗空树,或者是具有以下性质的树 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值 若它的右子树不为空,

    2024年02月04日
    浏览(51)
  • 【C++】数据结构的恶龙set和map来了~

    下一篇AVL树难点中的难点~   文章目录 前言 一、set的介绍 二、map的介绍        题目练习 总结   1.关联式容器 在初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque、 forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面

    2024年02月05日
    浏览(39)
  • 【数据结构】搜索树 与 Java集合框架中的Set,Map

    作者主页:paper jie_博客 本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。 本文录入于《JAVA数据结构》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力)打造,将javaSE基础知识一网打尽,希望可以帮到读者们哦。 其他专栏:《

    2024年02月08日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包