C++之map(键值对)和set(集合)

C++之map的基本操作总结:https://blog.csdn.net/google19890102/article/details/51720305

C++ Map常见用法说明:https://blog.csdn.net/shuzfan/article/details/53115922

C++中set用法详解:https://blog.csdn.net/yas12345678/article/details/52601454

map和unordered_map的差别和使用:https://blog.csdn.net/BillCYJ/article/details/78985895

unordered_map,unordered_set,map和set的用法和区别:https://blog.csdn.net/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.csdn.net/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的副本m
  • map<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指向的元素,返回void
  • m.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.csdn.net/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. 优点:1)有序性,这是map结构最大的优点,其元素的有序性在很多应用中都会简化很多的操作。2)红黑树,内部实现一个红黑书使得map的很多操作在lgn的时间复杂度下就可以实现,因此效率非常的高
  2. 缺点: 空间占用率高,因为map内部实现了红黑树,虽然提高了运行效率,但是因为每一个节点都需要额外保存父节点、孩子节点和红/黑性质,使得每一个节点都占用大量的空间
  3. 适用处:对于那些有顺序要求的问题,用map会更高效一些
     

unordered_map

  1. 优点: 因为内部实现了哈希表,因此其查找速度非常的快
  2. 缺点: 哈希表的建立比较耗费时间
  3. 适用处:对于查找问题,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.csdn.net/weixin_38391092/article/details/79586133
它底层使用平衡的搜索树——红黑树实现,插入删除操作时仅仅需要指针操作节点即可完成,不涉及到内存移动和拷贝,所以效率比较高。set的特性是,所有元素都会根据元素的键值自动排序,set的元素不像map那样可以同时拥有实值(value)和键值(key),set元素的键值就是实值,实值就是键值。set不允许两个元素有相同的键值。

set的各成员函数列表如下:

  1. begin()–返回指向第一个元素的迭代器

  2. clear()–清除所有元素

  3. count()–返回某个值元素的个数

  4. empty()–如果集合为空,返回true

  5. end()–返回指向最后一个元素的迭代器

  6. equal_range()–返回集合中与给定值相等的上下限的两个迭代器

  7. erase()–删除集合中的元素

  8. find()–返回一个指向被查找到元素的迭代器

  9. get_allocator()–返回集合的分配器

  10. insert()–在集合中插入元素

  11. lower_bound()–返回指向大于(或等于)某值的第一个元素的迭代器

  12. key_comp()–返回一个用于元素间值比较的函数

  13. max_size()–返回集合能容纳的元素的最大限值

  14. rbegin()–返回指向集合中最后一个元素的反向迭代器

  15. rend()–返回指向集合中第一个元素的反向迭代器

  16. size()–集合中元素的数目

  17. swap()–交换两个集合变量

  18. upper_bound()–返回大于某个值元素的迭代器

  19. value_comp()–返回一个用于比较元素间的值的函数

https://blog.csdn.net/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.csdn.net/yas12345678/article/details/52601454

  • 9
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值