複試上機指南-C++STL(一)

前言

最近在準備複試,博主的複試是C/C++,考慮到C++自帶的STL操作的方便性,所以選擇了用C++作爲機試的語言,預計會做成一個系列,內容包括但不限於:

  1. STL:set、map、序列容器(vector、deque、list、array)、string類、容器適配器(stack、queue、priority_queue)
  2. 常用的數據結構:鏈表、數組、棧、隊列、樹、圖等常用的數據結構,部分會在前面的STL中涉及,就不在這重複了。
  3. 常用算法:一是#incldue<alorithm>中包含的常用算法;二是關於貪心、動態規劃等等。
  4. 排序和搜索:會寫一些比較常見或者重要的排序冒泡、選擇、快排,線性搜索、二分搜索。因爲排序在算法中直接使用sort()即可,所以不會涉及太多,應對複試應該沒問題。

本系列會按照是什麼、爲什麼、常用的操作這三塊來行文,希望大家看完可以提出寶貴的意見。

關於本文

第一篇主要介紹的是STL中的set、map、序列容器。

1 set

1.1 是什麼

從現在開始我們將介紹set (集合)的使用。集合是一個簡單直觀的數學概念,即具有共同特徵的事物的集合。除了沒有單獨的鍵,set 容器和 map 容器很相似。定義 set 的模板有 4 種,其中兩種默認使用 less 來對元素排序,另外兩種使用哈希值來保存元素。有序 set 的模板定義在 set 頭文件中。既然是爲了實戰準備那麼我就只介紹平時用的比較多的一種。

1.2 爲什麼

在機試中常用的是set<T>這種,容器保存 T 類型的對象,而且保存的對象是唯一的,其中保存的元素是有序的,標粗的字體就是使用它的理由,在去除字符串、整數中重複的元素時,可以採取直接插入到set裏面,在插入的過程中就會自動去掉重複的元素,並且輸出的原有元素是按照從小到大拍好的(字符串按照asc||碼錶排序的)。

1.3 常用的操作

1.3.1 初始化

在使用時需要加上頭文件#include<set>//注意,STL頭文件沒有擴展名.h,代碼示例如下:

#include<iostream>   
 #include<set>  
using namespace std;
int main()
{
    set<int>s;
    return 0;
}

可對int進行修改,charstring都可以,在初始化結束後就可以利用s進行操作了。

1.3.2 插入、遍歷等常用操作

插入
 #include <iostream>
 #include <set>

 using namespace std;
 
 int main()
 {
     int a[] = {1,2,3};
     set<int> s;
     set<int>::iterator iter;
     s.insert(a,a+3);
     for(iter = s.begin() ; iter != s.end() ; ++iter)
     {
         cout<<*iter<<" ";
     }
     cout<<endl;
     return 0;
 }

上面這段程序中set<int>::iterator iter;其中iterator是迭代器,用來在插入、遍歷等操作中定位集合中元素的位置;在插入時可以是s.inset(1)代表將1插入到set中,而s.insert(a,a+3)是將數組a中的0-2的元素段插入到set中,我們平時在使用時,整數數組也好,字符數組也好或者是字符串,我們不會在一開始就賦好初值,往往都是根據題意自行輸入,所以我們需要循環來不斷地進行判斷進行輸入,例如:

while(i!=-1){
	cin>>i;
	s.insert(i);
}
遍歷

在輸出時,我們也會有固定的格式,比如上文中的:

 for(iter = s.begin() ; iter != s.end() ; iter++)
     {
         cout<<*iter<<" ";
     }

其中s.begin()返回set中的第一個元素;s.end()返回set中最後一個元素,用來判斷循環是否結束;在輸出時採用*iter類似於指針變量。

常用操作

下面介紹結幾個常用的函數:

clear():刪除容器中的所有元素
empty():判斷容器是否爲空
max_size():返回容器中可能包含的最大元素個數
size():返回當前容器中的元素的個數

使用的方法都是s.size()、s.max_size()、s.empty()
重點講解以下幾個erase()//刪除函數find()//查找函數equal_range()//返回一對定位器,分別表示第一個大於或等於給定關鍵值的元素和 第一個大於給定關鍵值的元素,這個返回值是一個pair類型,如果這一對定位器中哪個返回失敗,就會等於end()的值。最後一個還未遇到過。

set<int> numbers {2, 4, 6, 8, 10, 12, 14};
n = numbers.erase(12);//刪除12
n = numbers.erase(13);
numbers.clear();
set<int>::iterator iter;
numbers.erase(iter)//刪除定位器iterator指向的值。
numbers.erase(first,second)//刪除定位器first和second之間的值。
set<int> numbers {2, 4, 6, 8, 10, 12, 14};
numbers.find(2)//會返回2所在的位置,若沒有找到則會返回end()。

以上便是關於set常用的一些代碼了,想要掌握的話還是需要實操,後面我會陸續貼出一些題目,供大家練習。

2 map

2.1 是什麼

map是STL中的一個關聯容器,說白了就是鍵值對,map<K,T>其中第K可以稱爲關鍵字,每個關鍵字只能在map中出現一次,T稱爲該關鍵字的值,由於這個特性,它完成有可能在我們處理一對一數據的時候,在編程上提供快速通道。這裏說下map內部數據的組織,map內部自建一顆紅黑樹(一 種非嚴格意義上的平衡二叉樹),這顆樹具有對數據自動排序的功能,所以在map內部所有的數據都是有序的。

2.2 爲什麼

map會自動建立key-value對應,key和value可以是任意的你想要的類型,可以根據key值進行快速的查找,且查找速度相當快,比如有1000個記錄,最多查找10次,1000000記錄,最多查找20次;快速插入、快速刪除、根據key的值修改value的值、遍歷所有記錄。
所以掌握好map對我們在以後的做題中會節省很多時間,帶來方便的操作。

2.3 常用的操作

2.3.1 初始化

使用map得包含map類所在的頭文件#include <map> ,map對象是模板類,需要關鍵字和存儲對象兩個模板參數:map<int,string> m;這樣就定義了一個用int作爲索引,並擁有相關聯的指向string的指針。

2.3.2 插入、遍歷等常用操作

插入

下面介紹2種插入的方式:

  1. insert()函數插入
#include <map>  
#include <string>  
#include <iostream>   
using namespace std;   
int main()  
{   
    map<int, string> mapStudent;  
    mapStudent.insert(pair<int, string>(1, "student_one"));  
    mapStudent.insert(pair<int, string>(2, "student_two"));  
    mapStudent.insert(pair<int, string>(3, "student_three"));  
    map<int, string>::iterator iter;  
    for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)  
    	 cout<<iter->first<<' '<<iter->second<<endl;  
}  

2.用數組插入

#include <map>  
#include <string>  
#include <iostream>  
using namespace std;  
int main()   
{  
    map<int, string> mapStudent;  
    mapStudent[1] = "student_one";  
    mapStudent[2] = "student_two";  
    mapStudent[3] = "student_three";  
    map<int, string>::iterator iter;  
    for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)  
        cout<<iter->first<<' '<<iter->second<<endl;  
}  

同set類似,在定義了mapmap<int, string> mapStudent;之後,調用insert()函數進行插入,再插入的時候我們觀察到mapStudent.insert(pair<int, string>(1, "student_one"));是以1,“student_one”這樣的鍵值對進行插入的。這兩種插入的方法不同的地方在於,insert()在插入時,如果鍵值已經存在了就不能在繼續插入了,但是數組卻可以繼續插入,鍵值對應的新的value可以覆蓋之前的value。

遍歷
  1. 利用前向迭代器進行遍歷,也就是iter=mapStudent.begin();iter!=mapStudent.end();iter++。在遍歷時,同樣map<int, string>::iterator iter;寫好迭代器,for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)循環從第一個開始,到最後一個結束,在輸出時略有不同,cout<<iter->first<<' '<<iter->second<<endl;類似於指針分別輸出iter指向的first也就是key,second也就是value。
  2. 利用反向迭代器,for(iter = mapStudent.rbegin(); iter != mapStudent.rend(); iter++),rbegin()、rend()。
  3. 利用數組進行輸出for(int nindex = 1; nindex <= nSize; nindex++) cout<<mapStudent[nindex]<<endl;
大小

可以用size()函數,Int nSize = mapStudent.size();

查找獲取關鍵字

用find函數來定位數據出現位置,它返回的一個迭代器,當數據出現時,它返回數據所在位置的迭代器,如果map中沒有要查找的數據,它返回的迭代器等於end函數返回的迭代器。查找map中是否包含某個關鍵字條目用find()方法,傳入的參數是要查找的key,在這裏需要提到的是begin()和end()兩個成員,分別代表map對象中第一個條目和最後一個條目,這兩個數據的類型是iterator。

map<int, string> mapStudent;  
mapStudent.insert(pair<int, string>(1, "student_one"));  
mapStudent.insert(pair<int, string>(2, "student_two"));  
mapStudent.insert(pair<int, string>(3, "student_three"));  
map<int, string>::iterator iter;  
iter = mapStudent.find(1);  
if(iter != mapStudent.end())  
       cout<<"Find, the value is "<<iter->second<<endl;  
else  
       cout<<"Do not Find"<<endl;  
刪除元素

移除某個map中某個條目用erase()
該成員方法的定義如下:

iterator erase(iterator it);//通過迭代器對象刪除
iterator erase(iterator first,iterator last)//刪除一個範圍類似set
size_type erase(const Key&key);//通過關鍵字刪除
clear()就相當於enumMap.erase(enumMap.begin(),enumMap.end());

這裏要用到erase函數,它有三個重載了的函數,下面在例子中詳細說明它們的用法:

//如果要刪除1,用迭代器刪除  
  map<int, string>::iterator iter;  
  iter = mapStudent.find(1);  
  mapStudent.erase(iter);  
  //如果要刪除1,用關鍵字刪除  
  int n = mapStudent.erase(1);//如果刪除了會返回1,否則返回0  
  //用迭代器,成片的刪除  
  //一下代碼把整個map清空  
  mapStudent.erase( mapStudent.begin(), mapStudent.end() );  
  //成片刪除要注意的是,也是STL的特性,刪除區間是一個前閉後開的集合
排序

map中的元素是自動按Key升序排序,所以不能對map用sort函數;
這裏要講的是一點比較高深的用法了,排序問題,STL中默認是採用小於號來排序的,以上代碼在排序上是不存在任何問題的,因爲上面的關鍵字是int 型,它本身支持小於號運算,在一些特殊情況,比如關鍵字是一個結構體,涉及到排序就會出現問題,因爲它沒有小於號操作,insert等函數在編譯的時候過 不去,下面給出兩個方法解決這個問題。

#include <iostream>  
#include <string>  
#include <map>  
using namespace std;  
//結構體中重載<、==、>
typedef struct tagStudentinfo  
{  
       int   niD;  
       string   strName;  
       bool operator < (tagStudentinfo const& _A) const  
       {     //這個函數指定排序策略,按niD排序,如果niD相等的話,按strName排序  
            if(niD < _A.niD) return true;  
            if(niD == _A.niD)  
                return strName.compare(_A.strName) < 0;  
        return false;  
       }  
}Studentinfo, *PStudentinfo; //學生信息  
int main()  
{  
    int nSize;   //用學生信息映射分數  
    map<Studentinfo, int>mapStudent;  
    map<Studentinfo, int>::iterator iter;  
    Studentinfo studentinfo;  
    studentinfo.niD = 1;  
    studentinfo.strName = "student_one";  
    mapStudent.insert(pair<Studentinfo, int>(studentinfo, 90));  
    studentinfo.niD = 2;  
    studentinfo.strName = "student_two";  
    mapStudent.insert(pair<Studentinfo, int>(studentinfo, 80));  
    for (iter=mapStudent.begin(); iter!=mapStudent.end(); iter++)  
        cout<<iter->first.niD<<' '<<iter->first.strName<<' '<<iter->second<<endl;  
    return 0;  
}  

以上便是關於map的一些基本操作了,大家還是需要實操的,後面會貼出來一些代碼。這是個大佬。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章