C++之map的基本操作总结:https://blog.youkuaiyun.com/google19890102/article/details/51720305
C++ Map常见用法说明:https://blog.youkuaiyun.com/shuzfan/article/details/53115922
C++中set用法详解:https://blog.youkuaiyun.com/yas12345678/article/details/52601454
map和unordered_map的差别和使用:https://blog.youkuaiyun.com/BillCYJ/article/details/78985895
unordered_map,unordered_set,map和set的用法和区别:https://blog.youkuaiyun.com/zjajgyy/article/details/65935473
例题:
给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的绝对值最大为 k。输入: nums = [1,2,3,1,2,3], k = 2 输出: false
class Solution {
public:
bool containsNearbyDuplicate(vector<int>& nums, int k) {
/*for(int i=0;i<nums.size();i++)
for(int j=i+1;j<=i+k&&j<nums.size();j++){
if(nums[i]==nums[j])
return true;
}
return false;//超时*/
unordered_map<int, int> cnt;
for(int i=0; i<nums.size(); i++){
if(cnt.find(nums[i]) != cnt.end())
if(i - cnt[nums[i]] <= k)
return true;
cnt[nums[i]] = i;
}
return false;
}
};
集合(set)与映射(map)属于非线性结构容器类,内部实现上面是平衡二叉树
map集合(键值对)
C++中map提供的是一种键值对容器,里面的数据都是成对出现的。
每一对中的第一个值称之为关键字(key),每个关键字只能在map中出现一次;第二个称之为该关键字的对应值(value)。
1.pair类型
头文件:
- #include <utility>
初始化定义:
pair<T1, T2> p;
pair<T1, T2> p(v1,v2);
make_pair(v1,v2);
取出pair对象中的每一个成员的值:
p.first
p.second
#include <stdio.h>
#include <string.h>
#include <string>
#include <utility>
using namespace std;
int main(){
pair<int, string> p1(0, "Hello");
printf("%d, %s\n", p1.first, p1.second.c_str());
pair<int, string> p2 = make_pair(1, "World");
printf("%d, %s\n", p2.first, p2.second.c_str());
return 0;
}
另:以下内容与正文无关可直接跳到第二部分
string中c_str()的用法:https://blog.youkuaiyun.com/Makefilehoon/article/details/80687087
语法:const char *c_str();
c_str()函数返回一个指向正规C字符串的指针常量, 内容与本string串相同。
这是为了与c语言兼容,在c语言中没有string类型,故必须通过string类对象的成员函数c_str()把string 对象转换成c中的字符串样式。
注意:一定要使用strcpy()函数 等来操作方法c_str()返回的指针。
情况一:
错误示例:
char* c;
string s="1234";
c = s.c_str();
c最后指向的内容是垃圾,因为s对象被析构,其内容被处理,同时,编译器也将报错——将一个const char *赋与一个char *。
正确示例:
char c[20];
string s="1234";
strcpy(c,s.c_str());
这样才不会出错,c_str()返回的是一个临时指针,不能对其进行操作。
情况二:
c_str() 以 char* 形式传回 string 内含字符串,如果一个函数要求char*参数,可以使用c_str()方法:
string s = "Hello World!";
printf("%s", s.c_str()); // 输出 "Hello World!"
2.map对象
2.1 头文件以及初始化
头文件:
- #include <map>
初始化定义:
map<k, v> m; //
定义了一个名为m的空的map对象map<k, v> m(m2); //
创建了m2的副本mmap<k, v> m(b, e); //
创建了map对象m,并且存储迭代器b和e范围内的所有元素的副本
map的value_type是pair类型,键为const
//头文件
#include<map>
map<int, string> ID_Name;
// 使用{}赋值是从c++11开始的,因此编译器版本过低时会报错,如visual studio 2012
map<int, string> ID_Name = {
{ 2015, "Jim" },
{ 2016, "Tom" },
{ 2017, "Bob" } };
2.2 元素的插入
2.2.1 使用下标
map<int, string> ID_Name;
// 如果已经存在键值2015,则会作赋值修改操作,如果没有则插入
ID_Name[2015] = "Tom";
2.2.2 使用insert函数
m.insert(e) //
e是一个value_type类型的值m.insert(beg, end) //
beg和end标记的是迭代器的开始和结束m.insert(iter, e)
map里insert调用_M_t.insert_unique(_x)方法,该方法会首先遍历整个集合,判断是否存在相同的key,如果存在则直接返回,放弃插入操作。如果不存在才进行插入。
而[]方式是通过重载[]操作符来实现的,它直接进行插入或覆盖。
#include <stdio.h>
#include <map>
using namespace std;
int main(){
map<int, int> mp;
for (int i = 0; i < 10; i ++){
mp[i] = i;
}
for (int i = 10; i < 20; i++){
mp.insert(make_pair(i, i));
}
map<int, int>::iterator it;
for (it = mp.begin(); it != mp.end(); it++){
printf("%d-->%d\n", it->first, it->second);
}
return 0;
}
2.3 元素的查找和读取
- 采用下标的方法读取map中元素时,若map中不存在该元素,则会在map中插入
- 若只是查找该元素是否存在,可以使用函数
count(k)
,该函数返回的是k出现的次数 - 若是想取得key对应的值,可以使用函数
find(k)
,该函数返回的是指向该元素的迭代器
#include <stdio.h>
#include <map>
using namespace std;
int main(){
map<int, int> mp;
for (int i = 0; i < 20; i++){
mp.insert(make_pair(i, i));
}
if (mp.count(0)){
printf("yes!\n");
}else{
printf("no!\n");
}
map<int, int>::iterator it_find;
it_find = mp.find(0);
if (it_find != mp.end()){
it_find->second = 20;
}else{
printf("no!\n");
}
map<int, int>::iterator it;
for (it = mp.begin(); it != mp.end(); it++){
printf("%d->%d\n", it->first, it->second);
}
return 0;
}
删除元素:
函数是erase()
,该函数有如下的三种形式:
m.erase(k) //
删除m中键为k的元素,返回删除的元素的个数m.erase(p) //
删除迭代器p指向的元素,返回voidm.erase(b, e) //
删除迭代器b和迭代器e范围内的元素,返回void
#include <stdio.h>
#include <map>
using namespace std;
int main(){
map<int, int> mp;
for (int i = 0; i < 20; i++){
mp.insert(make_pair(i, i));
}
mp.erase(0);
mp.erase(mp.begin());
map<int, int>::iterator it;
for (it = mp.begin(); it != mp.end(); it++){
printf("%d->%d\n", it->first, it->second);
}
return 0;
}
map和unordered_map的比较
https://blog.youkuaiyun.com/BillCYJ/article/details/78985895
需要引入的头文件不同
map: #include < map >
unordered_map: #include < unordered_map >
内部实现机理不同
map: map内部实现了一个红黑树(红黑树是非严格平衡二叉搜索树,而AVL是严格平衡二叉搜索树),红黑树具有自动排序的功能,因此map内部的所有元素都是有序的,红黑树的每一个节点都代表着map的一个元素。因此,对于map进行的查找,删除,添加等一系列的操作都相当于是对红黑树进行的操作。map中的元素是按照二叉搜索树(又名二叉查找树、二叉排序树,特点就是左子树上所有节点的键值都小于根节点的键值,右子树所有节点的键值都大于根节点的键值)存储的,使用中序遍历可将键值按照从小到大遍历出来。
unordered_map: unordered_map内部实现了一个哈希表(也叫散列表,通过把关键码值映射到Hash表中一个位置来访问记录,查找的时间复杂度可达到O(1),其在海量数据处理中有着广泛应用)。因此,其元素的排列顺序是无序的。
优缺点以及适用处:
map:
- 优点:1)有序性,这是map结构最大的优点,其元素的有序性在很多应用中都会简化很多的操作。2)红黑树,内部实现一个红黑书使得map的很多操作在lgn的时间复杂度下就可以实现,因此效率非常的高
- 缺点: 空间占用率高,因为map内部实现了红黑树,虽然提高了运行效率,但是因为每一个节点都需要额外保存父节点、孩子节点和红/黑性质,使得每一个节点都占用大量的空间
- 适用处:对于那些有顺序要求的问题,用map会更高效一些
unordered_map
- 优点: 因为内部实现了哈希表,因此其查找速度非常的快
- 缺点: 哈希表的建立比较耗费时间
- 适用处:对于查找问题,unordered_map会更加高效一些,因此遇到查找问题,常会考虑一下用unordered_map
总结:
内存占有率的问题就转化成红黑树 VS hash表 , 还是unorder_map占用的内存要高。
但是unordered_map执行效率要比map高很多
对于unordered_map或unordered_set容器,其遍历顺序与创建该容器时输入的顺序不一定相同,因为遍历是按照哈希表从前往后依次遍历的。
map和unordered_map的使用
unordered_map的用法和map是一样的,提供了 insert,size,count等操作,并且里面的元素也是以pair类型来存贮的。其底层实现是完全不同的,上方已经解释了,但是就外部使用来说却是一致的。
补充:https://www.cnblogs.com/slothrbk/p/8823092.html
无论从查找、插入上来说,unordered_map的效率都优于hash_map,更优于map;而空间复杂度方面,hash_map最低,unordered_map次之,map最大。
存储时是根据key的hash值判断元素是否相同,即unordered_map内部元素是无序的,而map中的元素是按照二叉搜索树存储(用红黑树实现),进行中序遍历会得到有序遍历。所以使用时map的key需要定义operator<。而unordered_map需要定义hash_value函数并且重载operator==。但是很多系统内置的数据类型都自带这些。
总结:结构体用map重载<运算符,结构体用unordered_map重载==运算符。
set集合
https://blog.youkuaiyun.com/weixin_38391092/article/details/79586133
它底层使用平衡的搜索树——红黑树实现,插入删除操作时仅仅需要指针操作节点即可完成,不涉及到内存移动和拷贝,所以效率比较高。set的特性是,所有元素都会根据元素的键值自动排序,set的元素不像map那样可以同时拥有实值(value)和键值(key),set元素的键值就是实值,实值就是键值。set不允许两个元素有相同的键值。
set的各成员函数列表如下:
-
begin()–返回指向第一个元素的迭代器
-
clear()–清除所有元素
-
count()–返回某个值元素的个数
-
empty()–如果集合为空,返回true
-
end()–返回指向最后一个元素的迭代器
-
equal_range()–返回集合中与给定值相等的上下限的两个迭代器
-
erase()–删除集合中的元素
-
find()–返回一个指向被查找到元素的迭代器
-
get_allocator()–返回集合的分配器
-
insert()–在集合中插入元素
-
lower_bound()–返回指向大于(或等于)某值的第一个元素的迭代器
-
key_comp()–返回一个用于元素间值比较的函数
-
max_size()–返回集合能容纳的元素的最大限值
-
rbegin()–返回指向集合中最后一个元素的反向迭代器
-
rend()–返回指向集合中第一个元素的反向迭代器
-
size()–集合中元素的数目
-
swap()–交换两个集合变量
-
upper_bound()–返回大于某个值元素的迭代器
-
value_comp()–返回一个用于比较元素间的值的函数
https://blog.youkuaiyun.com/qq_29924041/article/details/74080102
begin 语法 | iterator begin() | 返回指向当前集合中第一个元素的迭代器 |
clear 语法 | void clear(); | 清除当前集合中的所有元素 |
count 语法 | size_type count( const key_type &key ); | 返回当前集合中出现的某个值的元素的数目 |
empty 语法 | bool empty(); | 如果当前集合为空,返回true;否则返回false |
end 语法 | const_iterator end(); | 返回指向当前集合中最后一个元素的迭代器 |
equal_range 语法 | pair equal_range( const key_type &key ); | 返回集合中与给定值相等的上下限的两个迭代器 |
erase 语法 | void erase( iterator i ); void erase( iterator start, iterator end ); size_type erase( const key_type &key ); | 删除i元素; 删除从start开始到end结束的元素; 删除等于key值的所有元素(返回被删除的元素的个数) |
find 语法 | iterator find( const key_type &key ); | 在当前集合中查找等于key值的元素,并返回指向该元素的迭代器; 如果没有找到,返回指向集合最后一个元素的迭代器。 |
insert 语法 | iterator insert( iterator i, const TYPE &val ); void insert( input_iterator start, input_iterator end ); pair insert( const TYPE &val ); | 在迭代器i前插入val; 将迭代器start开始到end结束返回内的元素插入到集合中; 在当前集合中插入val元素,并返回指向该元素的迭代器和一个布尔值来说明val是否成功的被插入了。 (应该注意的是在集合(Sets)中不能插入两个相同的元素。) |
lower_bound 语法 | iterator lower_bound( const key_type &key ); | 返回一个指向大于或者等于key值的第一个元素的迭代器。 |
key_comp 语法 | key_compare key_comp(); | 返回一个用于元素间值比较的函数对象。 |
max_size 语法 | size_type max_size(); | 返回当前集合能容纳元素的最大限值。 |
rbegin 语法 | reverse_iterator rbegin(); | 返回指向当前集合中最后一个元素的反向迭代器。 |
rend 语法 | reverse_iterator rend(); | 返回指向集合中第一个元素的反向迭代器。 |
size 语法 | size_type size(); | 返回当前集合中元素的数目。 |
swap 语法 | void swap( set &object ); | 交换当前集合和object集合中的元素。 |
upper_bound 语法 | iterator upper_bound( const key_type &key ); | 在当前集合中返回一个指向大于Key值的元素的迭代器。 |
value_comp 语法 | value_compare value_comp(); | 返回一个用于比较元素间的值的函数对象。 |
#include<set>
#include<iostream>
using namespace std;
int main()
{
int i;
int arr[5] = {0,1,2,3,4};
set<int> iset(arr,arr+5);
iset.insert(5);
cout<<"size:"<<iset.size()<<endl;
cout<<"3 count = "<<iset.count(3)<<endl;
iset.erase(1);
set<int>::iterator ite1 = iset.begin();
set<int>::iterator ite2 = iset.end();
for(;ite1!=ite2;ite1++)
{
cout<<*ite1;
}
cout<<endl;
ite1 = iset.find(3);
if(ite1!=iset.end())
cout<<"3 found"<<endl;
ite1 = iset.find(1);
if(ite1!=iset.end())
cout<<"1 not found"<<endl;
}
https://blog.youkuaiyun.com/yas12345678/article/details/52601454