【C++】关联容器学习记录
STL六大組件關系
Containe通過Allocator取得數據存儲空間,Algorithm通過Iterator存取Container,Functor內容可以協助Algorithm完成不同的策略變化,Adaptor可以修飾或者套接Functor。
關聯容器特性
1. 關聯容器定義
順序容器支持高效的關鍵字查找和訪問,map中的元素是一些關鍵字-值(key-value)對:關鍵字其索引作用,值則表示與縮影相關聯的數據。set中每個元素只包含一個關鍵字:set支持高效的關鍵字查詢操作。——檢查一個關鍵字是否在set中。(例如在文本處理中可以保存想要忽略的單詞,字典則是mapde 經典應用)。
| 關鍵字有序保存數組 | 特性 | 備注 |
|---|---|---|
| map | 關聯數組:保存關鍵字-值對 | |
| set | 關鍵字即值,即只保存關鍵字的容器 | |
| multimap | 可重復關鍵字 map | |
| multiset | 可重復關鍵字 set | |
| 關鍵字無序保存數組 | ||
| unordered_map | 哈希函數 map | |
| unordered_set | 哈希函數 set | |
| unordered_multimap | 關鍵字無序,哈希函數組織map | |
| unordered_multiset | 關鍵字無序,哈希函數組織set |
-
例子1:單詞計算程序
string strs[] = { "蘋果", "香蕉", "草莓", "蘋果", "香蕉", "香蕉", "香蕉", "蘋果", "香蕉", "草莓" };map<string, int> countmap;//pair<iterator,bool> insert (const value_type& val);//此類型的insert返回值是一個pair,第一個元素first是一個迭代器,指向新插入元素的map,第二個元素second是一個bool值,插入成功返回true,否則返回falsefor (const auto& e : strs){std::pair<std::map<std::string, int>::iterator, bool> ret = countmap.insert(make_pair(e, 1));if (ret.second == false){ret.first->second++;}}map<string, int>::iterator cit = countmap.begin();while (cit != countmap.end()){cout << cit->first << ":" << cit->second << endl;cit++;}//方式2string strs[] = { "蘋果", "香蕉", "草莓", "蘋果", "香蕉", "香蕉", "香蕉", "蘋果", "香蕉", "草莓" };map<string, int> countmap;//利用find統計每一個元素出現的次數for (const auto& e : strs){map<string, int>::iterator it = countmap.find(e);//countmap.end()是map中最后一個元素的下一個if (it != countmap.end()){//如果找到,value++;it->second++;}else{//若是沒有再去插入,又要遍歷一遍,有些許冗余countmap.insert(make_pair(e, 1));}}//遍歷每一個元素map<string, int>::iterator cit = countmap.begin();while (cit != countmap.end()){cout << cit->first << ":" << cit->second << endl;cit++;}方式3string strs[] = { "蘋果", "香蕉", "草莓", "蘋果", "香蕉", "香蕉", "香蕉", "蘋果", "香蕉", "草莓" };map<string, int> countmap;for (const auto& e : strs){//先調用operator[];再對返回值value進行++。countmap[e]++;}for (auto& e : countmap){cout << e.first << ":" << e.second << endl;} -
例子2經典的單詞計數程序—利用關聯數組map和互斥集合set
-
我們詳細講述了map這個關聯數組, 并介紹了經典的單詞計數程序,看看這個場景: 單詞計數的時候, 不考慮一系列的單詞, 如不考慮"abc", "de"等等。 set是個互斥集合, 所以在此可以排上用場了, 且看:
#pragma warning(disable : 4786)
#include <map>
#include <set>
#include <string>
#include <fstream>
#include <iostream>
using namespace std;int main()
{ifstream cin("test.txt"); // 這個cin會屏蔽掉std::cinif(!cin){return 1;}map<string, int> m;string word;set<string> s; // 互斥集合s.insert("abc");s.insert("de");while(cin >> word){if(s.find(word) == s.end()) // 在s中找不到word, 也就是說,word不在s中{m[word]++; // 非常非常經典啊, 我被map折服得五體頭地}}map<string, int>::iterator it;for(it = m.begin(); it != m.end(); it++){cout << (*it).first << " " << (*it).second << endl;}return 0;
}
markdown 圖片旋轉格式
<img src="C:\Users\Administrator\Desktop\pic\set-multiset.jpg" style= "transform: rotate(90deg); zoom: 200%;" />
set-multiset
map-multimap
pair 類型
-
- Write a program to read a sequence of strings and ints , storing each into a pair. Store the pairs in a vector. P381,11.12
-
make_pair 或者pair的4種初始化方式。
Exercise 11.12:#include <vector> #include <utility> #include <string> #include <iostream>int main() {std::vector<std::pair<std::string, int>> vec;std::string str;int i;while (std::cin >> str >> i)vec.push_back(std::pair<std::string, int>(str, i));//vec.push_back(std::make_pair(str, i));//vec.push_back({ str, i });//vec.emplace_back(str, i); //!! easiest way.for (const auto &p : vec)std::cout << p.first << ":" << p.second << std::endl; } -
- Define a map for which the key is the family’s last name (姓)and the value is a vector of the children’s names. Write code to add new families and to add new children to an existing family.
Exercise 11.7:#include <map>
#include <algorithm>using std::string; using std::vector; using std::map;
using std::cin; using std::cout;
using Families = map<string, vector<string>>;auto make_families()
{Families families;for (string ln; cout << "Last name:\n", cin >> ln && ln != "@q";)for (string cn; cout << "|-Children's names:\n", cin >> cn && cn != "@q";)families[ln].push_back(cn);return families;
}auto print(Families const& families)
{for (auto const& family : families){cout << family.first << ":\n";for (auto const& child : family.second)cout << child << " ";cout << "\n";}
}int main()
{print(make_families());return 0;
}
-
- Could we define a map from vector::iterator to int? What about from list::iterator to int? In each case, if not, why not?
// Exercise 11.10:// vector<int>::iterator to int is ok , because < is defined
// list<int>::iterator to int is not ok, as no < is defined.
-
- Extend the map of children to their family name that you wrote for the exercises in § 11.2.1 (p. 424) by having the vector store a pair that holds a child’s name and birthday.
// Exercise 11.14:
// Exercise 11.7:#include <iostream>#include <map>#include <string>#include <vector>using std::ostream;using std::cout;using std::cin;using std::endl;using std::string;
using std::make_pair;using std::pair;using std::vector;using std::map;class Families
{
public:using Child = pair<string, string>;using Children = vector<Child>;using Data = map<string, Children>;auto add(string const& last_name, string const& first_name, string birthday){auto child = make_pair(first_name, birthday);_data[last_name].push_back(child);}auto print() const{for (auto const& pair : _data){cout << pair.first << ":\n" ;for (auto const& child : pair.second)cout << child.first << " " << child.second << endl;cout << endl;}}private:Data _data;
};int main()
{Families families;auto msg = "Please enter last name, first name and birthday:\n";for (string l, f, b; cout << msg, cin >> l >> f >> b; families.add(l, f, b));families.print();return 0;
}
關聯容器操作
1、關聯容器和順序容器的本質區別:關聯容器是通過鍵存取和讀取元素、順序容器通過元素在容器中的位置順序存儲和訪問元素。
因此,關聯容器不提供front、push_front、pop_front、back、push_back以及pop_back,此外對于關聯容器不能通過容器大小來定義,因為這樣的話將無法知道鍵所對應的值什么。
2、兩個主要的關聯容器類型是map和set。map的元素以鍵-值對的形式組織:鍵用作元素在map的索引,而值則表示所存儲和讀取的數據。
set僅包含一個鍵,并有效地支持關于某個鍵是否存在的查詢。
set和map類型的對象不允許為同一個鍵添加第二個元素。
如果一個鍵必須對應多個實例,則需使用multimap或mutiset類型,這兩種類型允許多個元素擁有相同的鍵。
1.map
1-1. map的定義
1、定義一個map,必須指定關鍵字和值的類型;
2、從map中提取一個元素時,會得到一個pair類型的對象;
3、map中使用的pair用first成員保存關鍵字,用second成員保存對應的值;
1-2. map的插入
map<int,int> mymap;//下標直接創建mymap[5] = 9;mymap[8] = 3;//或者以下4種方式創建mymap.insert(make_pair(2,4));mymap.insert(pair<int,int>(11,7));mymap.insert(map<int,int>::value_type(4,20) );mymap.insert({5,6});//無法覆蓋前面的值,如果已經在map中,則什么都不做。 還是5,9//遍歷auto iter_map = mymap.begin();cout << "mymap_size:" << mymap.size() << endl;while( iter_map != mymap.end() ){cout << iter_map->first << ' ' << iter_map->second << endl;;iter_map++;}
1-3. map的遍歷
auto iter_map = mymap.begin();cout << "mymap_size:" << mymap.size() << endl;while( iter_map != mymap.end() ){cout << iter_map->first << ' ' << iter_map->second << endl;;iter_map++;}
1-4. map的查找
if (map.find(3) != map.end()) {cout << "find key=" << map.find(3)->first << ", value=" << map.find(3)->second << endl;}if (map.count(5) > 0) {cout << "find 5: " << map.count(5) << endl;}
1-5. map與unordered_map的區別及優缺點
如果把1-2節中的map替換成unordered_map,則后insert的在前面打印出來。
在這里插入圖片描述
區別:
map內部實現了一個紅黑樹(紅黑樹是非嚴格平衡二叉搜索樹,而AVL是嚴格平衡二叉搜索樹),紅黑樹具有自動排序的功能,因此map內部的所有元素都是有序的,紅黑樹的每一個節點都代表著map的一個元素。因此,對于map進行的查找,刪除,添加等一系列的操作都相當于是對紅黑樹進行的操作。map中的元素是按照二叉搜索樹(又名二叉查找樹、二叉排序樹,特點就是左子樹上所有節點的鍵值都小于根節點的鍵值,右子樹所有節點的鍵值都大于根節點的鍵值)存儲的,使用中序遍歷可將鍵值按照從小到大遍歷出來。
unordered_map內部實現了一個哈希表(也叫散列表,通過把關鍵碼值映射到Hash表中一個位置來訪問記錄,查找的時間復雜度可達到O(1),其在海量數據處理中有著廣泛應用)。因此,其元素的排列順序是無序的。
優缺點以及適用處
map:
優點:
有序性**,這是map結構最大的優點,其元素的有序性在很多應用中都會簡化很多的操作
紅黑樹,內部實現一個紅黑書使得map的很多操作在lgn的時間復雜度下就可以實現**,因此效率非常的高
缺點:
空間占用率高,因為map內部實現了紅黑樹,雖然提高了運行效率,但是因為每一個節點都需要額外保存父節點、孩子節點和紅/黑性質,使得每一個節點都占用大量的空間
適用處:對于那些有順序要求的問題,用map會更高效一些
unordered_map:
優點: 因為內部實現了哈希表,因此其查找速度非常的快
缺點: 哈希表的建立比較耗費時間
適用處:對于查找問題,unordered_map會更加高效一些,因此遇到查找問題,常會考慮一下用unordered_map
總結:
內存占有率的問題就轉化成紅黑樹 VS hash表 , 還是unorder_map占用的內存要高。
但是unordered_map執行效率要比map高很多
對于unordered_map或unordered_set容器,其遍歷順序與創建該容器時輸入的順序不一定相同,因為遍歷是按照哈希表從前往后依次遍歷的
2.set
2-1.set的定義
set就是關鍵字的簡單集合。當想知道一個值是否存在時,set是最有用的。
定義一個map時,必須即指明關鍵字類型又指明值類型;
而定義一個set時,只需指明關鍵字類型。
set中的key_type和value_type是一樣的;
set中保存的值就是關鍵字
2-2.set的插入
set的插入insert兩種方法:
1.接受一對迭代器
2.初始化列表
vector<int> ivec = { 2,4,6,8,2,4,6,8};
set<int> set2;
//1.接受一對迭代器
set2.insert(ivec.cbegin(),ivec.cend());
//2.初始化列表
set2.insert({1,3,5,7,1,3,5,7});
2-3.set的遍歷
**set<int> set_v;
set_v.insert( {1,7,8,5,6} );
set_v.insert(5);
set<int>::iterator s_iter = set_v.begin();
while( s_iter != set_v.end() )
{cout << *s_iter << endl;s_iter++;
}
**
2-4. unordered_set
unordered_set 是基于hash表的,因此并不是順序存儲。
迭代器
1. 插入器是一種適配器.
-
插入迭代器
back_inserter 創建一個push_back的迭代器。
fornt_inserter 創建一個push_front的迭代器
inserter創建一個使用insert的迭代器。
demo1
list<int> lst = { 1,2,3,4 }; list<int> lst2, lst3; copy(lst.begin(), lst.end(), front_inserter(lst2)); copy(lst.begin(), lst.end(), inserter(lst3,lst3.begin()));
2. iostream 迭代器
istream_iterator<T> in(is); in從輸入流is讀取類型為T的值
istream_iterator<T> end; 讀取類型為T的值istream_iterator迭代器,表示尾后位置in1 == in2 in1與in2必須讀取相同的類型,如果他們都是尾后迭代器,或綁定到相同的輸入,則兩者相同
in1 != in2
*in 返回從流中讀取的值
in->mem 與 (*it).mem 的含義相同
++in , in++ 前置版本返回一個指向遞增后迭代器的引用,后置版本返回舊值
istream_iterator調用 accumulate:
istream_iterator<int> in(cin), eof;
cout<< accumulate(in, eof, 0)<<endl;
3. ostream_iterator操作
- 方式1
ostream_iterator<int> out_iter(cout, " ");
for(auto e : vec)*out_iter++ = e;
cout<<endl;
- 方式2
當對out_iter賦值時,可以忽略解引用和遞增運算。即循環可以寫成如下的樣子
for (auto x : vec)out_iter = e;
cout<<endl;
推薦第一種方式,推薦方式1,原因是這種寫法 使得流迭代器和其他類型的迭代器保持一致。
反向迭代器
反向迭代器:++ 表示向前移動,–表示向后移動
vector<int> vec = { 1,2,3,4,5,6 };for (auto riter = vec.crbegin(); riter != vec.crend(); ++riter) {cout << *riter << endl;} // 6 5 4 3 2 1
sort(vec.rbegin(), vec.rend());// 1,2,3,4,5,6
泛型算法結構p365
總結
以上是生活随笔為你收集整理的【C++】关联容器学习记录的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 求一个qq情侣网名两个字。
- 下一篇: 【Smart_Point】动态内存与智能