STL概述
STL由一些可適應不同需求的集合類(collection class),以及在這些數據集合上操作的算法(algorithm)構成。
容器(Container) - 管理某類對象的集合
容器分爲序列式容器和關聯式容器,所有容器中存放的都是值而非引用。如果希望存放的不是副本,容器元素只能是指針。
迭代器(Iterator) - 在對象集合上進行遍歷
算法(Algorithm) - 處理集合內的元素
STL內的所有組件都由模板(template)構成,其元素可以是任意類型。
一定不要忘記<>!!
STL的區間一般都是左閉右開:[first,last)。
常用操作
與大小相關的操作
size()-返回當前容器的元素數量
empty()-判斷容器是否爲空
max_size()-返回容器能容納的最大元素數量
如果兩個容器內的所有元素按序相等,那麼這兩個容器相等(==),一般不作比較。
賦值和交換
swap()
與迭代器(iterator)相關的操作
begin()-返回一個迭代器,指向第一個元素
end()-返回一個迭代器,指向最後一個元素之後
rbegin()-返回一個逆向迭代器,指向逆向遍歷的第一個元素
rend()-返回一個逆向迭代器,指向逆向遍歷的最後一個元素之後
對元素操作
insert(pos,e)-將元素e的拷貝安插於迭代器pos所指的位置
erase(beg,end)-移除[beg,end]區間內的所有元素
clear()-移除所有元素
常用容器
vector
vector模擬動態數組,可以實現動態的增刪,也可以隨機的存取
map/multimap
使用平衡二叉樹管理元素,元素包含兩部分(key,value),key和value可以是任意類型。map容器是鍵-值對的集合。
map中不允許key相同的元素,multimap允許key相同的元素。
根據元素的key自動對元素排序,因此根據元素的key進行定位很快(樹形結構),因此可用作索引。
map集合元素插入後自動排序
set/multiset
使用平衡二叉樹管理元素,集合(Set)是一種包含已排序對象的關聯容器。set容器只是單純的鍵的集合。可用於判斷某值是否存在。
set中不允許key相同的元素,multiset允許key相同的元素。
set集合元素插入後自動排序
pair
pair模板可以用於生成 key-value對
實際使用
利用map進行查找
可以使用vector動態數組來存儲原始信息,當需要查找的時候如果直接查找的話要一個一個的從頭開始遍歷,效率較低,藉助map的鍵值對的特點,我們可以利用map來建立索引。
實現方法:在建立vector的同時,也同時將該元素插入到map容器中,map的first是待查找的關鍵字,second是該元素在vector中的下標。
如果是在導入數據時建立map,可以按照下面這樣寫:
vector.push_back(element);
mapname.insert(make_pair(element.getSname(), vector.size() - 1));
如果是在處理過程中構造或者重構map,按照以下的方式寫:
map1.clear();
map2.clear();
vector<T>::iterator it, first = vector.begin();
for (it = vector.begin(); it != vector.end(); it++)
{
map1.insert(make_pair(it->getSname(), distance(first, it)));
map2.insert(make_pair(it->getSno(), distance(first, it)));
//map1.insert(make_pair(it->getSname(), it - vector.begin()));
//map2.insert(make_pair(it->getSno(), it - vector.begin()));
}
在查找時,如果結果唯一:
void Operation::findBookById(string id)
{
multimap<string, int>::iterator it;
it = bookid.find(id);
if (it == bookid.end())
return;
else
cout << book[it->second];
}
如果結果不唯一:要用到lower_bound(滿足條件的下界)和upper_bound(滿足條件的下界)然後遍歷lower_bound到upper_bound之間
void Operation::findBookByName(string name)
{
multimap<string, int>::iterator it, p1, p2, p;
it = bookname.find(name);
if (it == bookname.end())
return;
else {
p1 = bookname.lower_bound(name);
p2 = bookname.upper_bound(name);
for (p = p1; p != p2; p++) {
cout << book[p->second] << endl;
}
}
}
要注意:一旦vector發生變化(如刪除了元素或者修改了元素的關鍵字),對應的map一定要相應變化,否則會使得map與vector之間不對應,查找時候會產生錯誤
而一般如果刪除了元素,map是進行重構;如果修改了關鍵字,map將此元素刪除後再在map的後面insert一個新的元素即可,因爲map自帶自動排序功能。
void Operation::rebuildBookMap()
{
bookname.clear();
bookid.clear();
vector<Book>::iterator it, first = book.begin();
for (it = book.begin(); it != book.end(); it++)
{
bookname.insert(make_pair(it->getBname(), distance(first, it)));
bookid.insert(make_pair(it->getIsbn(), distance(first, it)));
//bookname.insert(make_pair(it->getBname(), it-book.begin()));
//bookid.insert(make_pair(it->getIsbn(), it - book.begin()));
}
}
void Operation::modifyBook(string id)
{
multimap<string, int>::iterator it;
vector<Book>::iterator first = book.begin();
string pre;
it = bookid.find(id);
if (it == bookid.end())
return;
else
{
pre = book[it->second].getIsbn();
cin >> book[it->second];
int index = it->second;
if (book[it->second].getIsbn() != pre)
{
bookid.erase(it);
bookid.insert((make_pair(book[index].getIsbn(), index)));
}
cout << book[index];
}
}
利用find_if函數結合map實現模糊查找
string內部的find函數本身可以實現對任意子串的查找,也就實現了模糊查找。問題在於必須給map的first準確的關鍵字,否則會查不出來,這就需要對find_if函數的謂詞函數進行改造。
template<class InIt, class Pred>
InIt find_if(InIt first, InIt last, Pred pr);
返回區間 [first,last) 中的迭代器 i, 使得 pr(*i) == true
爲此,我構造了一個類,並重載了函數調用運算符,在其內部實現取子串查找。(STL中常常這樣構造類)
class map_value_finder
{
public:
map_value_finder(const string& cmp_string) :m_s_cmp_string(cmp_string) {}
bool operator ()(const multimap<string,int>::value_type& pair)
{
int loc;
loc=pair.first.find(m_s_cmp_string);
if (loc != pair.first.npos)
return true;
return false;
}
private:
const string& m_s_cmp_string;
};
void Operation::fuzzyFindBname(string str)
{
multimap<string, int>::iterator it, p1, p2, p, begin;
p1 = bookname.begin();
p2 = bookname.end();
begin = p1;
for (p = p1; p != p2; p++)
{
it = find_if(begin, bookname.end(), map_value_finder(str));
if (it == bookname.end())
return;
else
cout << book[it->second] << endl;
it++;
begin = it;
}
}
區間查找
可以利用lower_bound到upper_bound來實現上界和下界的值的確定,然後遍歷其中的元素即可
void Operation::findBookByTime(string t1, string t2)
{
multimap<string, int>::iterator it1, it2, p;
it1 = booktime.lower_bound(t1);
it2 = booktime.upper_bound(t2);
for (p = it1; p != it2; p++)
{
cout << book[p->second] << endl;
}
}
多條件組合查詢
利用集合的交併集來實現,將所有的關鍵字全部查出來,取出其值(second)放入集合中,將多個條件產生的集合做交操作即可。
交操作的函數前四個參數分別代表待操作的集合的起始和結束點,最後一個參數表示將操作後的集合保存到哪裏。
set_intersection(bpub.begin(), bpub.end(), bname.begin(), bname.end(), insert_iterator<vector<int>>(res, res.begin()));
void Operation::multifind(string str1,string str2)
{
vector<int> res;
vector<int>::iterator v;
multiset<int> bpub;
multiset<int> bname;
multimap<string, int>::iterator it, p1, p2, p;
it = bookpub.find(str1);
if (it == bookpub.end())
return;
else {
p1 = bookpub.lower_bound(str1);
p2 = bookpub.upper_bound(str1);
for (p = p1; p != p2; p++)
{
bpub.insert(p->second);
}
}
it = bookname.find(str2);
if (it == bookname.end())
return;
else {
p1 = bookname.lower_bound(str2);
p2 = bookname.upper_bound(str2);
for (p = p1; p != p2; p++)
{
bname.insert(p->second);
}
}
set_intersection(bpub.begin(), bpub.end(), bname.begin(), bname.end(), insert_iterator<vector<int>>(res, res.begin()));
for (v = res.begin(); v != res.end(); v++)
{
cout << book[*v]<<" ";
}
}
感想
首先反思一下最近的學習狀態,最近的學習狀態不是很好,白天樓下的鄰居在裝修,搞得在家裏學習上課都心煩意亂的,寫代碼也是不太容易進入狀態,所以一氣之下搬到了樓下車庫裏,在車庫學習雖然聽不見噪音,但還是沒有學習的環境,而且天天搬來搬去的,弄得白天很不在狀態;只有晚上夜深了才能更好的靜下心來學習,所以我最近熬夜熬到很晚,熬到兩三點都是常見的事,但是隨之而來的就是生物鐘的紊亂,搞得該吃飯的時候不吃飯,該睡覺的時候不睡覺,很是難受。通過這個事情我知道了有些環境是改變不了的,能改變的只有自己。這學期可能是我大學四年課業壓力最大的一年,學分已經修到爆,光主課就有9門,每一門都不簡單,另外還有項目方面的事情需要操心,還正巧攤上了疫情,所以在是時間上是空前的緊張,今後的學習還是要進一步提高效率,在家學習雖然靈活度更高,但是一定要自己安排好時間,挺過最難的這一學期。
接下來談談最近的收穫,最近收穫挺大的,學到的知識應該要學會用,上課聽是一碼事,會用是另一碼事。就比如現在學的這個STL,STL是別人已經寫好的一些工具,簡單的說我們會使用即可,也就是我們只要學習其參數的含義會調用就行,但是從更深層次來說,我們作爲計算機專業的學生,需要深究這個黑箱內部的東西。一方面,只是看過一遍或者只是簡單使用一遍,印象不夠深刻,過上一段時間可能就忘得一乾二淨,這就需要我們去真正把學過的東西正在應用起來,比如可以利用STL來進行系統的開發,利用STL進行開發不僅在代碼量上更加簡潔,而且STL本身提供的算法一般來說效率都比較高,另一方面,我們還要了解STL內部實現的原理,知其然更要知其所以然嘛,更有利於我們後續的學習。比如假期裏我學了matlab,僅僅是看過一遍,沒怎麼上手,這兩天老師一問就慌了,從寒假到現在短短兩個月的時間就差不多全忘了,所以我們學習知識一定要學會實際使用。也就是what、how、why中重點掌握how。在這個學習過程中一定會出現各種各樣的問題,但是我感覺現在的代碼的每一個字母都要自己敲上,每出現一個bug都要自己找錯、改錯,這樣從一開始就積累自己的開發經驗,雖然可能耗費的時間會多一些,但是打好了基礎,我相信是一定可以厚積薄發的。
最後寫一下我的開發經驗,關於文件的系統開發流程在上一篇博客C++文件操作專題提過了,簡單來說就是,從文件讀入數據到內存->內存處理->將處理的數據寫回文件。這其實和我們大一開始熟悉的編程思想是一樣的,只不過把從鍵盤輸入變成了從文件讀入,把到顯示屏輸出換成到文件輸出,基本上都是“三段式”的感覺。下面談一下最近的開發感想,其實學到現在我感覺開發的過程並不是很難,我們所做的編程說白了就是把人的思想告訴計算機,描述的方法也就是我們所用的編程語言構成的算法,要想開發出好程序,一開始人的思想一定要清晰,最好自己腦子裏先有一個框架,根據這個框架或者說是流程,把我們所想的用計算機語言描述出來,這也就是簡單系統的開發,所以,人的思路是編程中最重要的,整體框架一定要實現規劃好,具體實現的時候需要用到什麼算法、什麼知識點就考察我們的編程基礎了,這個其實也需要我們有一個清晰的思路,把整個系統完整的實現出來。接下來就到了debug階段,這個階段應該是耗時間最長的,可能一個晚上就能把主體代碼搞定,但是改錯調試要好幾天,這個階段我剛剛說過,需要自己不斷的摸索,是需要積累經驗的,見得多了自然改的就快了。最後測試的時候也要注意各種情況儘量都要考慮到,比如上次的delete函數沒有重構的問題,當時delete函數我寫在了最後,所以自然不會在delete之後出現異常,但是如果我再換一下位置,這個問題可能我當時就會發現。測試的時候我也儘量從使用者的角度去考慮,從使用者的角度會不會覺得某些地方有點彆扭,或者某些地方的設計並不是很合理,也就是說測試的階段在一定程度上起到了優化的作用。這就是我最近軟件開發的心得吧,前面的路還有很長,希望我保重身體,保持奮鬥的心,進無止境,精益求精,加油!