STL
是C++
的標准模板庫,英文全稱是Standard Template Library
,它稍微有點復雜,操作很多,但是非常實用。STL的目的是標准化常用的組件,這樣就不用重新開發了,可以使用現成的組件來提高編程效率。它是由Alexander Stepanov
等人在惠普實驗室工作時所開發出來的,從根本上說,STL
是vector
,set
,map
等容器的集合。
本實訓主要設置了六個關卡來教學和實踐vector
,set
和map
:
- 第一關:介紹向量
vector
的相關操作並設計了一些任務,同時需要調用vector
的相關操作來完成; - 第二關:設計了一個二維動態數組的應用,使用
vector
能夠非常方便解決該問題; - 第三關:介紹集合
set
的相關操作,並運用這些基本操作解決設定的小任務; - 第四關:基於
set
求解一段英語文本中的出現的26
個英文字母(區分大小寫); - 第五關:介紹鍵值對映射
map
的一些操作,運用map
維護一個學生成績管理系統,並提供查詢功能; - 第六關:運用鍵值對映射
map
統計一段英語文本中的26
個英文字母出現的次數(區分大小寫)。
第1關:STL模板之動態數組:向量vector的操作
任務描述
本關任務:仔細閱讀下文向量vector
的相關操作,並使用vector
完成對n
個整數序列的插入、刪除和排序功能。
相關知識
為了完成本關任務,你需要掌握:1.向量的概念;2.插入元素;3.刪除元素;4.基於sort
對向量排序;5.遍歷向量;6.清空向量。
向量的概念
向量vector
:是一種順序容器,與數組類似,但它比數組更優越。數組不能動態拓展,在程序運行的時候可能造成內存浪費和訪問越界。而vector
正好可以彌補這一缺陷,可動態分配和拓展內存,它的隨機訪問快,在中間插入和刪除慢,但在末端插入和刪除快。
向量作為基本數組的類模板,被包含在vector
頭文件中,定義方式如下(注意搭配algorithm
和using namespace std
一起使用):
vector<int> v1
,定義一個元素為類型為int
整型的向量v1
vector<string> v2(10)
,定義一個元素為類型為string
字符串類型的向量v2
,初始存儲空間大小為10
,每個元素初始為空串vector<node> v3
,定義一個元素為類型為node
類型的向量v3
,其中node
一般是結構體等自定義數據類型
插入元素
往向量插入一個元素通過調用push_back()
方法實現(在向量末尾插入),另外也可以通過下標訪問的方式直接在指定位置插入元素(前提是該位置已經被分配內存空間),如下實例:
1 vector <int> vec; // 創建一個整型向量vec 2 vec.push_back(1); // 向vec插入一個元素1 3 vec.push_back(2); // 向vec插入一個元素2 4 vec[1] = 3; // 直接在位置1插入元素3,原來的元素2被元素3覆蓋了 5 // 目前vec包含 1, 3兩個元素
刪除元素
向量的刪除與插入相對應,包含隊尾刪除和指定位置刪除:隊尾刪除通過調用pop_back()
方法,注意,它並不會返回被刪除的元素;指定位置的刪除是基於迭代器iterator
實現的,迭代器相對應數組的指針,指向向量的存儲地址,通過調用erase(iterator pos)
方法刪除迭代器位置pos
所在的元素,基於上述vec
的實例如下:
1 vec.pop_back(); // 刪除了元素3 2 vector<int>::iterator pos = vec.begin(); //定義一個vector<int>的迭代器pos,並指向vec的首地址 3 cout<<*pos; // 與指針一樣,通過*訪問地址上的值,輸出為1 4 vec.erase(pos); // 刪除迭代器地址pos及其元素,目前pos為vec首地址,元素值為1,刪除之后元素為空,不包含任何元素了
基於sort
對向量排序
向量是基於數組的類模板,同樣可以用sort
函數完成排序,使用方式如下:
sort(vec.begin(), vec.end()); // 默認從小到大排序
遍歷向量
向量的遍歷可以通過下標訪問和迭代器訪問的方式:
1 for(int i=0;i<vec.size();i++) // size()返回當前向量vec的大小 2 cout<<vec[i]; 3 for(vector<int>::iterator it=vec.begin();it!=vec.end();it++) 4 cout<<*it;
清空向量
向量的清空通過調用clear()
方法實現,清空后向量大小變為0
:
1 vec.clear()
編程要求
本關的編程任務是補全右側代碼片段main
中Begin
至End
中間的代碼,具體要求如下:
- 創建一個整型類型的向量
vec
; - 讀取數據:序列個數
n
,以及n
個整數並插入向量vec
; - 通過
erase
操作刪除向量vec
中的重復元素:保留第一次出現的元素,刪除之后出現的重復元素; - 使用
Algorithm
模板函數sort
對向量vec
里的元素從小到大排序; - 遍歷向量
vec
並輸出:元素中間空格隔開,末尾加換行符\n
; - 調用
clear
清空向量。
測試說明
平台將自動編譯補全后的代碼,並生成若干組測試數據,接着根據程序的輸出判斷程序是否正確。
以下是平台的測試樣例:
測試輸入:
7
1 2 3 1 2 3 4
預期輸出:
1 2 3 4
0
輸入格式:
第一行:序列個數n
第二行:n個整數序列
輸出格式:
第一行:遍歷並輸出向量,中間空格隔開,末尾換行\n
第二行:非學員輸出,數值0用於檢測向量是否清空
開始你的任務吧,祝你成功!

1 // 2 // main.cpp 3 // step1 4 // 5 // Created by ljpc on 2018/7/23. 6 // Copyright ? 2018年 ljpc. All rights reserved. 7 // 8 9 #include <iostream> 10 #include <algorithm> 11 #include <vector> 12 using namespace std; 13 14 int main(int argc, const char * argv[]) { 15 16 17 // 請在這里補充代碼,完成本關任務 18 /********* Begin *********/ 19 // 1.構建整型向量vec 20 vector<int> vec; 21 22 // 2.讀取數據:序列個數n,以及n個整數並存入向量vec 23 int n; 24 cin>>n; 25 for(int i=0; i<n; i++) { 26 int m; 27 cin>>m; 28 vec.push_back(m); 29 } 30 31 // 3.刪除向量vec中的重復元素 32 sort(vec.begin(),vec.end()); 33 auto last = std::unique(vec.begin(),vec.end()); 34 vec.erase(last,vec.end()); 35 36 37 // 4.使用Algorithm模板函數sort對vec排序:從小到大 38 sort(vec.begin(),vec.end()); 39 40 // 5.遍歷向量vec並輸出,元素中間空格隔開,末尾加換行符'\n' 41 for(vector<int>::iterator it=vec.begin();it!=vec.end();it++) cout<<" "<<*it; 42 cout<<endl; 43 44 // 6.清空向量vec 45 vec.clear(); 46 47 /********* End *********/ 48 printf("%d\n", int(vec.size())); 49 50 return 0; 51 }
第2關:STL模板之動態數組:向量vector的應用
任務描述
本關任務:在一座城池中有N個谷倉,每個谷倉分別有Ni袋糧食並堆成了一列,重量分別為Wj,為了應對外敵,通常需要在谷倉間調動糧食,每次調動只會選擇糧堆最上面的一袋糧食運走,並放在目的谷倉糧堆的最上面。
相關知識
為了完成本關任務,你需要掌握:1.向量的相關操作,2.嵌套使用向量模擬二維動態數組。
向量的相關操作
糧食調動是在糧堆的最上面進行的操作,涉及的向量操作為push_back
和pop_back
,詳細的向量具體操作和運用請參考STL模板之動態數組:向量vector的操作。
嵌套使用向量模擬二維動態數組
谷倉的數量在不同的測試用例下是不同的,每個谷倉中的糧食袋數是動態變化的,因此,可以使用二維的動態數組來求解,既方便又節約內存。
- 第一步:定義一個以糧食重量
int
為數據類型的動態數組vector<int>
; - 第二步:定義一個以糧食重量向量
vector<int>
為數據類型的動態數組vector<vector<int> > vec
。
這樣,vec
的第一維下標就表示谷倉的ID
,第二維就是對應谷倉的各袋糧食的重量。
編程要求
本關的編程任務是補全右側代碼片段main
中Begin
至End
中間的代碼,具體要求如下:
-
創建一個
vector<vector<int> > vec
二維動態向量,讀取各個谷倉的初始糧食狀況; -
基於向量的
push_pack
和pop_back
等基本操作,實現M次調動糧食; -
輸出最后各個谷倉
ID
、該谷倉中各袋糧食的重量以及總的重量(請嚴格遵循測試樣例的輸出格式)。
測試說明
平台將自動編譯補全后的代碼,並生成若干組測試數據,接着根據程序的輸出判斷程序是否正確。
以下是平台的測試樣例:
測試輸入:
5
3 79 32 29
4 42 41 92 6
6 68 37 63 11 34 79
2 63 36
3 27 74 67
6
move 1 2
move 3 0
move 3 0
move 4 2
move 1 4
move 2 0
預期輸出:
0 79 32 29 36 63 67 306
1 42 41 83
2 68 37 63 11 34 79 6 298
3 0 0
4 27 74 92 193
輸入格式:
第一行整數N:谷倉數量
接下來N行,依次為各個谷倉ID=[0,1,...,N−1]的初始信息:第一個數字為該谷倉的初始糧食袋數Ni,后面接着Ni袋糧食的重量Wj
接下來一行整數M:調動糧食次數
接下來M行,每行move a b:將谷倉a糧堆的最后一袋糧食調動到谷倉b糧堆的最上面
輸出格式:
輸出N行,按順序輸出谷倉ID=[0,1,...,N−1]的最終狀態信息:ID W0 W1 ... Wj Wsum,若谷倉空了,則輸出為ID 0 0
注意:每行所有數字中間空格隔開,末尾換行\n
開始你的任務吧,祝你成功!

1 // 2 // main.cpp 3 // step2 4 // 5 // Created by ljpc on 2018/7/23. 6 // Copyright © 2018年 ljpc. All rights reserved. 7 // 8 9 #include <iostream> 10 #include <algorithm> 11 #include <vector> 12 using namespace std; 13 14 int main(int argc, const char * argv[]) { 15 16 17 // 請在這里補充代碼,完成本關任務 18 /********* Begin *********/ 19 vector<vector<int> > vec; 20 int n; 21 scanf("%d", &n); 22 while (n--) { 23 vector<int> tmp; 24 int m; 25 scanf("%d", &m); 26 while (m--) { 27 int x; 28 scanf("%d", &x); 29 tmp.push_back(x); 30 } 31 vec.push_back(tmp); 32 } 33 int q; 34 scanf("%d", &q); 35 while (q--) { 36 char str[20]; 37 int a, b; 38 scanf("%s %d %d", str, &a, &b); 39 if(!vec[a].empty()){ 40 vec[b].push_back(vec[a].back()); 41 vec[a].erase(vec[a].end()-1); 42 } 43 } 44 for (int i=0; i<vec.size(); i++) { 45 int tot = 0; 46 printf("%d ", i); 47 if (vec[i].empty()) { 48 printf("0 "); 49 } 50 else{ 51 for (int j=0; j<vec[i].size(); j++) { 52 printf("%d ",vec[i][j]); 53 tot += vec[i][j]; 54 } 55 } 56 printf("%d\n", tot); 57 } 58 }
第3關:STL模板之關聯容器:集合set的操作詳解
任務描述
本關任務:仔細閱讀下文集合的相關操作,並使用set
完成N次插入或刪除元素操作,以及集合的遍歷和查找等要求。
相關知識
為了完成本關任務,你需要掌握:1.集合的概念,2.插入元素,3.刪除元素,4.遍歷集合,5.查找元素,6.清空集合。
集合的概念
集合set
就是數學上的集合,其中的每個元素沒有重復的,但是set
中的元素在數據結構中是有序存儲的(默認升序),為了高效的實現插入、刪除和查找等操作,這與數學上的集合中元素無序性有點區別。
集合set
也是STL
中的一種標准關聯容器,其底層數據結構是基於平衡搜索樹(紅黑樹)實現的,插入刪除等操作都是通過迭代器指針實現的,不涉及內存操作,因此效率非常高。
集合set
被包含在set
頭文件中,基本定義方式如下:
set<int> st
,定義了一個元素類型為int
整型的集合st
插入元素
往集合中插入一個元素通過調用insert()
方法實現:
1 set<int> st; // 創建一個整型集合st 2 st.insert(1); // 向st插入一個元素1 3 st.insert(2); // 向st插入一個元素2
刪除元素
集合元素的刪除通過調用erase()
方法實現,傳入的參數可以是待刪除的元素,也可以是待刪除元素的地址:
1 set<int>::iterator it = st.begin(); // 定義一個迭代器,初始為st的首地址 2 cout<<*it; // 輸出為元素1 3 st.erase(it); // 刪除it指向的元素1 4 st.erase(2); // 刪除元素2
遍歷集合
集合的遍歷通過迭代器的方式進行,首先讓迭代器指針指向集合的首地址,然后逐步移動迭代器指針,直到集合的尾地址:
1 for(set<int>::iterator it = st.begin();it!=st.end();it++) 2 cout<<*it;
查找元素
在集合中查找指定元素通過find()
方法實現,若找到了則返回該元素在集合中的地址,否則返回集合的尾地址:
1 it = st.find(2); //查找指定元素2,it結果為st.end(),因為2已經被刪除了
集合清空
集合的清空通過調用clear()
方法實現,清空后集合的大小st.size()
變為0
:
1 st.clear() 2 cout<<st.size(); // 結果為0
編程要求
本關的編程任務是補全右側代碼片段main
中Begin
至End
中間的代碼,具體要求如下:
-
創建一個空的集合
st
,數據類型為int
; -
讀取數據:第一行整數N,后面N行插入或刪除操作,按指定要求輸出相應信息;
-
遍歷集合:首先在一行輸出集合的大小,然后在下一行輸出集合所有元素,中間空格隔開,末尾換行;
-
讀取數據:整數M以及M次查找操作,並按指定要求輸出相應信息;
-
清空集合
測試說明
平台將自動編譯補全后的代碼,並生成若干組測試數據,接着根據程序的輸出判斷程序是否正確。
以下是平台的測試樣例:
測試輸入:
7
insert 2
insert 7
insert 1
erase 3
erase 1
insert 5
erase 7
2
find 7
find 5
預期輸出:
3 not in set
print set: 2
2 5
find 7 not in set
find 5 in set
0
輸入格式:
第一行整數N
接下來N行插入或刪除操作
整數M
接下來M行查找操作
輸出格式:
插入刪除階段:若待刪除的元素x不在集合中,則輸出:x not in set
遍歷集合階段輸出兩行,第一行print set: set.size(),第二行集合所有元素x1 x2 x3 ...
查找階段:若找到元素x,則輸出find x in set,否則輸出find x not in set
非學員輸出0
開始你的任務吧,祝你成功!

1 // 2 // main.cpp 3 // step5 4 // 5 // Created by ljpc on 2018/7/25. 6 // Copyright 2018年 ljpc. All rights reserved. 7 // 8 9 #include <iostream> 10 #include <cstring> 11 #include <string> 12 #include <algorithm> 13 #include <map> 14 using namespace std; 15 16 int main(int argc, const char * argv[]) { 17 18 19 // 請在這里補充代碼,完成本關任務 20 /********* Begin *********/ 21 // 1.創建一個空的鍵值對映射mp: 鍵string->值int 22 map<string,int> mp; 23 // 2.讀取和處理數據:插入n個學生信息(姓名 成績),並按指定要求輸出 24 int n1,i; 25 cin>>n1; 26 string s1,s2; 27 int n2; 28 pair<string,int> p; 29 map<string,int>::iterator it; 30 for(i=0;i<n1;i++){ 31 cin>>s1>>s2>>n2; 32 if(s1=="insert"){ 33 p.first=s2; 34 p.second=n2; 35 it=mp.find(p.first); 36 if(it==mp.end()) 37 mp.insert(p); 38 else 39 cout<<s2<<" has been recorded\n"; 40 } 41 } 42 /*if(it==mp.end()){ 43 for(i=0;i<n1;i++){ 44 p.first=s2; 45 p.second=n2; 46 mp.insert(p); 47 } 48 } 49 else 50 cout<<s2<<" has been recorded\n";*/ 51 52 // 3.讀取和處理數據:刪除m個學生信息(姓名),並按指定要求輸出 53 int n3; 54 cin>>n3; 55 string s3,s4; 56 /*cin>>s3; 57 if(s3=="erase"){ 58 cin>>s4; 59 p.first=s4; 60 it=mp.find(s4); 61 if(it!=mp.end()){ 62 for(i=0;i<n3;i++){ 63 mp.erase(s4); 64 } 65 } 66 else 67 cout<<s4<<" has not been recorded\n"; 68 }*/ 69 for(i=0;i<n3;i++){ 70 cin>>s3>>s4; 71 if(s3=="erase"){ 72 p.first=s4; 73 74 it=mp.find(p.first); 75 if(it!=mp.end()) 76 mp.erase(p.first); 77 else 78 cout<<s4<<" has not been recorded\n"; 79 } 80 81 } 82 // 4.遍歷映射mp,並按指定要求輸出 83 cout<<"print map: "<<mp.size()<<"\n"; 84 for(it=mp.begin();it!=mp.end();it++){ 85 cout<<it->first<<" "<<it->second<<"\n"; 86 } 87 // 5.查找q個學生信息,並按指定要求輸出 88 int n4; 89 cin>>n4; 90 string s5,s6; 91 92 for(i=0;i<n4;i++){ 93 cin>>s5>>s6; 94 if(s5=="find"){ 95 it=mp.find(s6); 96 if(it!=mp.end()){ 97 cout<<s6<<" score "<<it->second<<"\n"; 98 } 99 else 100 cout<<s6<<" cannot been found\n"; 101 } 102 } 103 104 // 6.清空集合 105 mp.clear(); 106 /********* End *********/ 107 printf("%d\n", int(mp.size())); 108 109 return 0; 110 }