STL三 map find和count
用来查找
1 2 3 4 5 6 map<int ,string>::iterator it = m.find (key); if (it != m.end ()){ cout << it.first <<", " << it.second; } m.count (key);
和set基本一致 ,只是要注意,find的返回值是一个pair类型
insert
与set基本相同,也可以进行插入一个元素或一个迭代器范围的,或者大括号范围的元素,与set的insert的不同之处是,待插入元素value_type是一个pair类型 返回值是一个pair<map<value_type,value_type>::iterator,bool>
类型
1 2 3 4 5 6 7 8 9 10 pair<map<value_type,value_type>::iterator,bool > ret = m.insert ({key,value}); if (it.second){ cout << it.first.first << ":" << it.first.second << endl; }else { }
下标操作 下标非索引,而是key值,通过key值找到相应的value值,也就是说下标具备查找的功能,如果查找的key是存在的,可以把对应的value找出来,如果key不存在,则会把一个空的value插入进去与该key对应起来,也就是下标操作具备插入功能,此外,还可以进行修改,通过key找到value是可以修改的
1 2 const map<key,value> tmp = {{k,v}};tmp[0 ];
map存储自定义类型 1 2 3 4 5 6 7 class Point { ... }; void test () { map<string,Point> m = {{"hello" ,Point (1 ,2 )},{"world" ,Point (3 ,4 )},make_pair ("haha" ,Point (5 ,6 )),pair <string,Point>("gigity" ,Point (7 ,8 ))}; }
这里自定义类型存在了value,所以初始化是没有问题的,但是如果key值存的自定义类型,那么就要像set一样提供一个排序方法
multimap 与map的区别就是key值可以是重复的
insert 与map也有区别,因为返回值类型不一样,multimap的insert的返回值是一个迭代器,而map的返回值是一个pair,因为map的key可以是重复的,所以pair的第二个bool值一定是true,所以不需要了,只保留了第一个迭代器
不支持下标操作 multimap是没有下标操作的,因为multimap的key值是可以重复的的,而下标传递的是key类型,所以可能出现二义性
总结: 1.关联式容器的底层实现是红黑树
2.关联式容器中的元素是有序的
3.关联式容器中只有map具备下标
无序关联式容器
底层实现是哈希表
哈希相关的概念 哈希函数 通过key值找到在哈希表中的位置值
构建函数 定址法:H(key)=a*key+b 平方取中法:key^2=1234^2=1522756 —–>227 数字分析法:H(key)=key %10000; 除留取余法:H(key)=key mod p(p<=m,m为表长)
哈希冲突 不同的key值通过哈希函数映射之后,对应的位置值是一样的,这叫哈希冲突
解决哈希冲突的方式 开放定址法 链地址法(推荐使用这种,这也是STL中使用的方法) 再散列法 建立公共溢出区
装载因子 装载因子 = 元素的个数/表长
如果装载因子的值比较大,那么说明空间的利用率比较高,同时冲突的概率也会比较高,如果装载因子的值比较小,那么说明空间的利用率比较低,同时冲突的概率也会比较低
unordered_set 构造函数 与set,vector类似,也有五种初始化的方法
1 2 unordered_set<int > us = {2 ,3 ,5 ,6 ,1 ,6 ,8 ,5 ,3 ,2 }; print (us);
unordered_set存放的是key类型,key值是唯一的,不能重复 key值是没有顺序的(如存入时 的顺序也是不一样的,是通过哈希映射来确定下标的)
其他操作 查找count, find , 插入insert, 删除erase与set是完全一样的,也没有下标操作
针对于自定义类型的写法 存入自定义类型,需要自定义hash方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 namespace std{ template <> struct hash <Point> { size_t operator () (const Point &rhs,const Point &rhs) const { return (rhs.getX () << 1 ) ^ (rhs.getY ()<<2 ); } }; } namespace std{ template <> struct equal_to <Point> { bool operator () (const Point &lsh,const Point &rhs) const { return (lhs.getX () == rhs.getX () && lhs.getY () && rhs.getY ()); } }; } unordered_set<Point> number = { Point (1 ,2 ), Point (3 ,4 ), Point (5 ,6 ) }
使用函数对象的方法特化哈希方法 1 2 3 4 5 6 7 struct HashPoint { size_t operator () (const Point &rhs,,const Point &rhs) const { return (rhs.getX () << 1 ) ^ (rhs.getY ()<<2 ); } };
keyEqual的其他方法 1 2 3 4 5 6 struct EqualToPoint { bool operator () (const Point &rhs,,const Point &rhs) const { return (lhs.getX () == rhs.getX () && lhs.getY () && rhs.getY ()); } };
1 2 3 4 bool operator ==(const Point &lhs, const Point &rhs){ return (lhs.getX () == rhs.getX () && lhs.getY () && rhs.getY ()); }
模板的特化和运算符重载同时存在的情况下优先执行模板的特化
unordered_map 基本特征 与map基本一致,但是底层是用哈希表实现的,插入顺序与存储位置无关
其他操作 查找插入删除完全一样
下标操作 也是可以进行下标操作的
针对自定义类型 key存储自定义类型也需要特化Hash,KeyEqual
key存储的是string类型的话不需要特化Hash,keyEqual,因为string以及提供好了
下午练习 Q:无序关联式容器有哪些?各自具有哪些特点?
A:无序关联式容器有:
unordered_map unordered_set
unordered_set 不允许有重复值,底层用哈希表实现,所以存储的位置与插入的顺序无关,所以没有取下表操作,支持快速查找和修改操作
unordered_map 不允许出现重复的key值,value值可以重复,底层用哈希表实现,存储位置和插入的顺序无关,但这个可以取下标,支持快速查找和修改操作。
Q:使用unordered_map重写词频统计作业。再比较一下使用map和vector时所花费的时间,体会这几种容器的区别
A:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 #include <iostream> #include <unordered_map> #include <string> #include <fstream> #include <memory> #include <sstream> #include <algorithm> using namespace std;struct IFstreamDeleter { void operator () (ifstream *ptr) { if (ptr){ ptr->close (); } } }; struct OFstreamDeleter { void operator () (ofstream *ptr) { if (ptr){ ptr->close (); } } }; class StaticWord {public : StaticWord (string openfilename, string writefilename) :_ifs(new ifstream(openfilename,ios::in),IFstreamDeleter ()) ,_ofs(new ofstream (writefilename), OFstreamDeleter ()) { static_word (); WriteToFile (); } void search (string word) { if (_dict.find (word) != _dict.end ()){ cout << word << "出现了" << _dict[word] << "次" <<endl; }else { cout << "该单词没有出现" << endl; } } private : void WriteToFile () { for (auto &e : _dict){ *_ofs << e.first << ":" << e.second << endl; } } void static_word () { stringstream content; content << _ifs->rdbuf (); istringstream iss (content.str()) ; string line; string word; while (getline (iss, line)){ ToWord (line); istringstream w (line) ; while (w >> word){ ++_dict[word]; } } } void ToWord (string &str) { for (int i = 0 ; i < str.size (); ++i){ if (!isalpha (str[i])){ str[i] = ' ' ; }else { str[i] = tolower (str[i]); } } } private : unordered_map<string ,int > _dict; shared_ptr<ifstream> _ifs; shared_ptr<ofstream> _ofs; int _fileLength; }; int main (int argc, char *argv[]) { if (argc < 3 ){ cout << "请输入读入的文件名和输出的文件名" << endl; } else { StaticWord sw (argv[1 ],argv[2 ]); } return 0 ; }
Q:使用unordered_map/map实现单词转换程序。给定一个string,将它转换为另一个string。程序的输入是两个文件,第一个文件保存的是一些规则,用来转换第二个文件中的文本。每条规则由两部分组成:一个可能出现在输入文件中的单词和一个用来替换它的短语。表达的含义是,每当第一个单词出现在输入中时,我们就将它替换为对应的短语,第二个输入文件包含要转换的文本。(C++ primer 11.3.6)
提示:
规则文件:map.txt文件,其实就是第一个单词,被后面的一串所替换。
待转换文本:file.txt文件,该文件中的单词如果出现在map.txt文件的第一个单词的话,就用map.txt的后面一串替代。
结果:最后结果其实就是,将file.txt文件中的并且出现在map.txt中第一个单词转换为map.txt后面的一串。例如:where r u的输出结果就是where are you. r替换为are,u替换为you
//file.txt where r u y dont u send me a pic k thk l8r
//map.txt brb be right back k okay? y why r are u you pic picture thk thanks! l8r later
A:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 #include <memory> #include <fstream> #include <sstream> #include <iostream> #include <map> using namespace std;struct FstreamDeleter { void operator () (fstream *ptr) { if (ptr){ ptr->close (); delete ptr; } } }; struct IFstreamDeleter { void operator () (ifstream *ptr) { if (ptr){ ptr->close (); delete ptr; } } }; class ReplaceWord {public : ReplaceWord (string openfilename,string replaceFilename) :_ifs_map(new ifstream(openfilename,ios::in),IFstreamDeleter ()) ,_ifs_rep(new fstream (replaceFilename,ios::in | ios::out),FstreamDeleter ()) { stringstream content; content << _ifs_map->rdbuf (); istringstream iss (content.str()) ; string line; while (getline (iss,line)){ int flag = 0 ; int num = 0 ; string key,value; for (int i = 0 ; i < line.size (); ++i){ if (line[i] == ' ' ){ flag = 1 ; num++; } if (num == 1 ){ num++; continue ; } if (flag == 0 ){ key += line[i]; }else { value +=line[i]; } } cout << key <<":" << value << endl; _replace_rule[key] = value; } Replace (); } private : void Replace () { stringstream content; content << _ifs_rep->rdbuf (); istringstream iss (content.str()) ; string newContent; string line, word; while (getline (iss,line)){ cout << line << endl; istringstream w (line) ; while (w >> word){ if (_replace_rule.find (word) != _replace_rule.end ()){ newContent = newContent +_replace_rule[word] +' ' ; }else { newContent = newContent + word + ' ' ; } } newContent += '\n' ; } cout << newContent << endl; _ifs_rep->seekg (0 ,ios::beg); _ifs_rep->write (newContent.c_str (),newContent.size ()); } private : shared_ptr<ifstream> _ifs_map; shared_ptr<fstream> _ifs_rep; map<string,string> _replace_rule; }; int main (int argc, char * argv[]) { if (argc < 3 ){ cout<< "输入的参数不够" << endl; }else { ReplaceWord rw (argv[1 ], argv[2 ]); } return 0 ; }