這是微軟幫助文檔中對集合(set)的解釋: “描述了一個控制變長元素序列的對象(注:set中的key和value是Key類型的,而map中的key和value是一個pair結構中的兩個分 量)的模板類,每一個元素包含了一個排序鍵(sort key)和一個值(value)。對這個序列可以進行查找、插入、刪除序列中的任意一個元素,而完成這些操作的時間同這個序列中元素個數的對數成比例關 系,並且當游標指向一個已刪除的元素時,刪除操作無效。”
而一個經過更正的和更加實際的定義應該是:一個集合(set)是一個容器,它其中所包含的元素的值是唯一的。這在收集一個數據的具體值的時候是有用的。集 合中的元素按一定的順序排列,並被作為集合中的實例。如果你需要一個鍵/值對(pair)來存儲數據,map是一個更好的選擇。一個集合通過一個鏈表來組 織,在插入操作和刪除操作上比向量(vector)快,但查找或添加末尾的元素時會有些慢。
#include<iostream> #include<string> #include<set> using namespace std; int main() { set<string> strset; set<string>::iterator iter; strset.insert("apple"); strset.insert("orange"); strset.insert("grapes"); strset.insert("grapes"); for(iter=strset.begin();iter!=strset.end();iter++) { cout<<*iter<<endl; } }
輸出:
apple
grapes
orange
//注意:輸出的集合中的元素是按字母大小順序排列的,而且每個值都不重復。
如果你感興趣的話,你可以將輸出循環用下面的代碼替換:
copy(strset.begin(), strset.end(), ostream_iterator<string>(cout, " "));
另一個例子:
#include<iostream> #include<set> using namespace std; struct stu{ char a[10]; int s; }; class stu1:greater<stu> { public: bool operator () (stu b1,stu b2) const{ return b1.s<b2.s; } }; main() { set<stu,stu1 > a; for(int i=0;i<3;i++){ stu d; cin>>d.a>>d.s; a.insert(d); } set<stu,stu1 >::iterator l; for(l=a.begin();l!=a.end();l++){ cout<<l->a<<" "<<l->s<<endl; } }
注意:set第二個參數為仿函數,不能寫成函數。
我在類中寫成員函數operator()報錯,不知為什么。
/* set/multiset會根據待定的排序准則,自動將元素排序。兩者不同在於前者不允許元素重復,而后者允許。 1) 不能直接改變元素值,因為那樣會打亂原本正確的順序,要改變元素值必須先刪除舊元素,則插入新元素 2) 不提供直接存取元素的任何操作函數,只能通過迭代器進行間接存取,而且從迭代器角度來看,元素值是常數 3) 元素比較動作只能用於型別相同的容器(即元素和排序准則必須相同) set模板原型://Key為元素(鍵值)類型 template <class Key, class Compare=less<Key>, class Alloc=STL_DEFAULT_ALLOCATOR(Key) > 從原型可以看出,可以看出比較函數對象及內存分配器采用的是默認參數,因此如果未指定,它們將采用系統默認方式, 另外,利用原型,可以有效地輔助分析創建對象的幾種方式 */ #include <iostream> #include <string> #include <set> using namespace std; struct strLess { bool operator() (const char *s1, const char *s2) const { return strcmp(s1, s2) < 0; } }; void printSet(set<int> s) { copy(s.begin(), s.end(), ostream_iterator<int>(cout, ", ") ); // set<int>::iterator iter; // for (iter = s.begin(); iter != s.end(); iter++) // //cout<<"set["<<iter-s.begin()<<"]="<<*iter<<", "; //Error // cout<<*iter<<", "; cout<<endl; } void main() { //創建set對象,共5種方式,提示如果比較函數對象及內存分配器未出現,即表示采用的是系統默認方式 //創建空的set對象,元素類型為int, set<int> s1; //創建空的set對象,元素類型char*,比較函數對象(即排序准則)為自定義strLess set<const char*, strLess> s2( strLess); //利用set對象s1,拷貝生成set對象s2 set<int> s3(s1); //用迭代區間[&first, &last)所指的元素,創建一個set對象 int iArray[] = {13, 32, 19}; set<int> s4(iArray, iArray + 3); //用迭代區間[&first, &last)所指的元素,及比較函數對象strLess,創建一個set對象 const char* szArray[] = {"hello", "dog", "bird" }; set<const char*, strLess> s5(szArray, szArray + 3, strLess() ); //元素插入: //1,插入value,返回pair配對對象,可以根據.second判斷是否插入成功。(提示:value不能與set容器內元素重復) //pair<iterator, bool> insert(value) //2,在pos位置之前插入value,返回新元素位置,但不一定能插入成功 //iterator insert(&pos, value) //3,將迭代區間[&first, &last)內所有的元素,插入到set容器 //void insert[&first, &last) cout<<"s1.insert() : "<<endl; for (int i = 0; i <5 ; i++) s1.insert(i*10); printSet(s1); cout<<"s1.insert(20).second = "<<endl;; if (s1.insert(20).second) cout<<"Insert OK!"<<endl; else cout<<"Insert Failed!"<<endl; cout<<"s1.insert(50).second = "<<endl; if (s1.insert(50).second) {cout<<"Insert OK!"<<endl; printSet(s1);} else cout<<"Insert Failed!"<<endl; cout<<"pair<set<int>::iterator::iterator, bool> p;\np = s1.insert(60);\nif (p.second):"<<endl; pair<set<int>::iterator::iterator, bool> p; p = s1.insert(60); if (p.second) {cout<<"Insert OK!"<<endl; printSet(s1);} else cout<<"Insert Failed!"<<endl; //元素刪除 //1,size_type erase(value) 移除set容器內元素值為value的所有元素,返回移除的元素個數 //2,void erase(&pos) 移除pos位置上的元素,無返回值 //3,void erase(&first, &last) 移除迭代區間[&first, &last)內的元素,無返回值 //4,void clear(), 移除set容器內所有元素 cout<<"\ns1.erase(70) = "<<endl; s1.erase(70); printSet(s1); cout<<"s1.erase(60) = "<<endl; s1.erase(60); printSet(s1); cout<<"set<int>::iterator iter = s1.begin();\ns1.erase(iter) = "<<endl; set<int>::iterator iter = s1.begin(); s1.erase(iter); printSet(s1); //元素查找 //count(value)返回set對象內元素值為value的元素個數 //iterator find(value)返回value所在位置,找不到value將返回end() //lower_bound(value),upper_bound(value), equal_range(value) 略 cout<<"\ns1.count(10) = "<<s1.count(10)<<", s1.count(80) = "<<s1.count(80)<<endl; cout<<"s1.find(10) : "; if (s1.find(10) != s1.end()) cout<<"OK!"<<endl; else cout<<"not found!"<<endl; cout<<"s1.find(80) : "; if (s1.find(80) != s1.end()) cout<<"OK!"<<endl; else cout<<"not found!"<<endl; //其它常用函數 cout<<"\ns1.empty()="<<s1.empty()<<", s1.size()="<<s1.size()<<endl; set<int> s9; s9.insert(100); cout<<"s1.swap(s9) :"<<endl; s1.swap(s9); cout<<"s1: "<<endl; printSet(s1); cout<<"s9: "<<endl; printSet(s9); //lower_bound,upper_bound,equal_range(略) } ///////////////i測試結果///////////////////////// s1.insert() : 0, 10, 20, 30, 40, s1.insert(20).second = Insert Failed! s1.insert(50).second = Insert OK! 0, 10, 20, 30, 40, 50, pair<set<int>::iterator::iterator, bool> p; p = s1.insert(60); if (p.second): Insert OK! 0, 10, 20, 30, 40, 50, 60, s1.erase(70) = 0, 10, 20, 30, 40, 50, 60, s1.erase(60) = 0, 10, 20, 30, 40, 50, set<int>::iterator iter = s1.begin(); s1.erase(iter) = 10, 20, 30, 40, 50, s1.count(10) = 1, s1.count(80) = 0 s1.find(10) : OK! s1.find(80) : not found! s1.empty()=0, s1.size()=5 s1.swap(s9) : s1: 100, s9:
所有的STL容器
容器(Container)的概念的出現早於模板(template),它原本是一個計算機科學領域中的一個重要概念,但在這里,它的概念和STL混合在一起了。下面是在STL中出現的7種容器:
vector(向量)——STL中標准而安全的數組。只能在vector 的“前面”增加數據。
deque(雙端隊列double-ended queue)——在功能上和vector相似,但是可以在前后兩端向其中添加數據。
list(列表)——游標一次只可以移動一步。如果你對鏈表已經很熟悉,那么STL中的list則是一個雙向鏈表(每個節點有指向前驅和指向后繼的兩個指針)。
set(集合)——包含了經過排序了的數據,這些數據的值(value)必須是唯一的。
map(映射)——經過排序了的二元組的集合,map中的每個元素都是由兩個值組成,其中的key(鍵值,一個map中的鍵值必須是唯一的)是在排序 或搜索時使用,它的值可以在容器中重新獲取;而另一個值是該元素關聯的數值。比如,除了可以ar[43] = "overripe"這樣找到一個數據,map還可以通過ar["banana"] = "overripe"這樣的方法找到一個數據。如果你想獲得其中的元素信息,通過輸入元素的全名就可以輕松實現。
multiset(多重集)——和集合(set)相似,然而其中的值不要求必須是唯一的(即可以有重復)。
multimap(多重映射)——和映射(map)相似,然而其中的鍵值不要求必須是唯一的(即可以有重復)。
注意:如果你閱讀微軟的幫助文檔,你會遇到對每種容器的效率的陳述。比如:log(n*n)的插入時間。除非你要處理大量的數據,否則這些時間的影響是可 以忽略的。如果你發現你的程序有明顯的滯后感或者需要處理時間攸關(time critical)的事情,你可以去了解更多有關各種容器運行效率的話題。
怎樣在一個map中使用類?
Map是一個通過key(鍵)來獲得value(值)的模板類。
另一個問題是你希望在map中使用自己的類而不是已有的數據類型,比如現在已經用過的int。建立一個“為模板准備的(template-ready)”類,你必須確保在該類中包含一些成員函數和重載操作符。下面的一些成員是必須的:
缺省的構造函數(通常為空)
拷貝構造函數
重載的”=”運算符
你應該重載盡可能多的運算符來滿足特定模板的需要,比如,如果你想定義一個類作為 map中的鍵(key),你必須重載相關的運算符。但在這里不對重載運算符做過多討論了。
//程序:映射自定義的類。
//目的:說明在map中怎樣使用自定義的類。
#include <string>
#include <iostream>
#include <vector>
#include <map>
using namespace std;
class CStudent
{
public :
int nStudentID;
int nAge;
public :
//缺省構造函數——通常為空
CStudent() { }
// 完整的構造函數
CStudent(int nSID, int nA) { nStudentID=nSID; nAge=nA; }
//拷貝構造函數
CStudent(const CStudent& ob)
{
nStudentID=ob.nStudentID; nAge=ob.nAge; }
// 重載“=”
void operator = (const CStudent& ob)
{
nStudentID=ob.nStudentID; nAge=ob.nAge;
}
};
int main(int argc, char* argv[])
{
map <string, CStudent> mapStudent;
mapStudent["Joe Lennon"] = CStudent(103547, 22);
mapStudent["Phil McCartney"] = CStudent(100723, 22);
mapStudent["Raoul Starr"] = CStudent(107350, 24);
mapStudent["Gordon Hamilton"] = CStudent(102330, 22);
// 通過姓名來訪問Cstudent類中的成員
cout << "The Student number for Joe Lennon is " <<
(mapStudent["Joe Lennon"].nStudentID) << endl;
return 0;
}
TYPEDEF
如果你喜歡使用typedef關鍵字,下面是個例子:
typedef set <int> SET_INT;
typedef SET_INT::iterator SET_INT_ITER
編寫代碼的一個習慣就是使用大寫字母和下划線來命名數據類型。
ANSI / ISO字符串
ANSI/ISO字符串在STL容器中使用得很普遍。這是標准的字符串類,並得到了廣泛地提倡,然而在缺乏格式聲明的情況下就會出問題。你必須使用“<<”和輸入輸出流(iostream)代碼(如dec, width等)將字符串串聯起來。
參考了:http://www.cppblog.com/Plator/archive/2008/06/11/52906.html
可在必要的時候使用c_str()來重新獲得字符指針。
set是關聯容器。其鍵值就是實值,實值就是鍵值,不可以有重復,所以我們不能通過set的迭代器來改變set的元素的值,set擁有和list相同的特
性:當對他進行插入和刪除操作的時候,操作之前的迭代器依然有效。當然刪除了的那個就沒效了。set的底層結構是RB-tree,所以是有序的。
stl中特別提供了一種針對set的操作的算法:交集set_intersection,並集set_union,差集set_difference。對稱差集set_symeetric_difference,這
些算法稍后會講到。
一:set模板類的聲明。
template <
class Key,
class Traits=less<Key>,
class Allocator=allocator<Key>
>
class set。
其中個參數的意義如下:
key:要放入set里的數據類型,可以是任何類型的數據。
Traits:這是一個仿函數(關於仿函數是什么,我后面的文章會講到)。提供了具有比較功能的仿函數,來覺得元素在set里的排列的順序,這是
一個可選的參數,默認的是std::less<key>,如果要自己提供這個參數,那么必須要遵循此規則:具有兩個參數,返回類型為bool。
Allocator:空間配置器,這個參數是可選的,默認的是std::allocator<key>.
二:set里的基本操作
我們可以通過下面的方法來實例化一個set對象
std::set<int> s;那個s這個對象里面存貯的元素是從小到大排序的,(因為用std::less作為比較工具。)
如果要想在s里面插入數據,可以用inset函數(set沒用重載[]操作,因為set本生的值和索引是相同的)
s.insert(3);s.insert(5).....
因為set是集合,那么集合本身就要求是唯一性,所以如果要像set里面插入數據和以前的數據有重合,那么插入不成功。
可以通過下面的方法來遍歷set里面的元素
std::set<int>::iterator it = s.begin();
while(it!=s.end())
{
cout<<*it++<<endl;//迭代器依次后移,直到末尾。
}
如果要查找一個元素用find函數,it = s.find(3);這樣it是指向3的那個元素的。可以通過rbegin,rend來逆向遍歷
std::set<int>::reverse_iterator it = s.rbegin();
while(it!=s.rend())
{cout<<*it++<<endl;}
還有其他的一些操作在這就不一一列出了。
三:與set相關的一組算法
set_intersection() :這個函數是求兩個集合的交集。下面是stl里的源代碼
template<class _InIt1,
class _InIt2,
class _OutIt> inline
_OutIt set_intersection(_InIt1 _First1, _InIt1 _Last1,
_InIt2 _First2, _InIt2 _Last2, _OutIt _Dest)
{ // AND sets [_First1, _Last1) and [_First2, _Last2), using operator<
for (; _First1 != _Last1 && _First2 != _Last2; )
if (*_First1 < *_First2)
++_First1;
else if (*_First2 < *_First1)
++_First2;
else
*_Dest++ = *_First1++, ++_First2;
return (_Dest);
}
這是個模板函數,從上面的算法可以看出,傳進去的兩個容器必須是有序的。_Dest指向輸出的容器,這個容器必須是預先分配好空間的,否則
會出錯的,返回值指向保存結果的容器的尾端的下一個位置。eg.
set_union() :求兩個集合的並集,參數要求同上。
std::set_difference():差集
set_symmetric_difference():得到的結果是第一個迭代器相對於第二個的差集並上第二個相當於第一個的差集。代碼:
struct compare
{
bool operator ()(string s1,string s2)
{
return s1>s2;
}///自定義一個仿函數
};
int main()
{
typedef std::set<string,compare> _SET;
_SET s;
s.insert(string("sfdsfd"));
s.insert(string("apple"));
s.insert(string("english"));
s.insert(string("dstd"));
cout<<"s1:"<<endl;
std::set<string,compare>::iterator it = s.begin();
while(it!=s.end())
cout<<*it++<<" ";
cout<<endl<<"s2:"<<endl;
_SET s2;
s2.insert(string("abc"));
s2.insert(string("apple"));
s2.insert(string("english"));
it = s2.begin();
while(it!=s2.end())
cout<<*it++<<" ";
cout<<endl<<endl;
string str[10];
string *end = set_intersection(s.begin(),s.end(),s2.begin(),s2.end(),str,compare());//求交集,返回值指向str最后一個元素的尾端
cout<<"result of set_intersection s1,s2:"<<endl;
string *first = str;
while(first<end)
cout <<*first++<<" ";
cout<<endl<<endl<<"result of set_union of s1,s2"<<endl;
end = std::set_union(s.begin(),s.end(),s2.begin(),s2.end(),str,compare());//並集
first = str;
while(first<end)
cout <<*first++<<" ";
cout<<endl<<endl<<"result of set_difference of s2 relative to s1"<<endl;
first = str;
end = std::set_difference(s.begin(),s.end(),s2.begin(),s2.end(),str,compare());//s2相對於s1的差集
while(first<end)
cout <<*first++<<" ";
cout<<endl<<endl<<"result of set_difference of s1 relative to s2"<<endl;
first = str;
end = std::set_difference(s2.begin(),s2.end(),s.begin(),s.end(),str,compare());//s1相對於s2的差集
while(first<end)
cout <<*first++<<" ";
cout<<endl<<endl;
first = str;
end = std::set_symmetric_difference(s.begin(),s.end(),s2.begin(),s2.end(),str,compare());//上面兩個差集的並集
while(first<end)
cout <<*first++<<" ";
cout<<endl;
}
set<int> s3 ;
set<int>::iterator iter = s3.begin() ;
set_intersection(s1.begin(),s1.end(),s2.begin(),s2.end(),inserter(s3,iter));
copy(s3.begin(),s3.end(), ostream_iterator<int>(cout," "));