如何使用STL關聯式容器 ---- set、multiset

關聯式容器及鍵值對

STL中,像vector、list、deque、forward_list(C++11)等,這些容器統稱爲序列式容器,因爲其底層爲線性序列的數據結構,裏面存儲的是元素本身

STL中,關聯式容器也是用來存儲數據的,與序列式容器不同的是,其裏面存儲的是<key, value>結構的鍵值對,在數據檢索時比序列式容器效率更高

鍵值對是用來表示具有一 一對應關係的一種結構,該結構中一般只包含兩個成員變量key和value,key代表鍵值,value表示與key對應的信息。就比如:現在要建立一個英漢互譯的字典,那該字典中必然有英文單詞與其對應的中文含義,而且,英文單詞與其中文含義是一 一對應的關係,即通過該應該單詞,在詞典中就可以找到與其對應的中文含義。

SGI-STL中關於鍵值對的定義:

template <class T1, class T2>
struct pair
{
	typedef T1 first_type;
	typedef T2 second_type;
	T1 first;
	T2 second;
	pair()
		: first(T1())
		, second(T2())
	{}
	pair(const T1& a, const T2& b)
		: first(a)
		, second(b)
	{}
};

根據應用場景的不桶,STL總共實現了兩種不同結構的管理式容器:樹型結構與哈希結構

樹型結構的關聯式容器主要有四種:map、set、multimap、multiset

這四種容器的共同點是:使用平衡搜索樹(即紅黑樹)作爲其底層結果,容器中的元素是一個有序的序列。

此篇文章先來看看set以及multiset這兩個容器。
 


set

基本概念--> 

  1. set是按照一定次序存儲元素的容器
  2. 在set中,元素的value也標識它(value就是key,類型爲T),並且每個value必須是唯一的。set中的元素不能在容器中修改(元素總是const),但是可以從容器中插入或刪除它們。
  3. 在內部,set中的元素總是按照其內部比較對象(類型比較)所指示的特定嚴格弱排序準則進行排序。
  4. set容器通過key訪問單個元素的速度通常比unordered_set容器慢,但它們允許根據順序對子集進行直接迭代。
  5. set在底層是用二叉搜索樹(紅黑樹)實現的。 

插入 ---> 

        set<int> s;

	s.insert(4);
	s.insert(5);
	s.insert(5);
	s.insert(1);
	s.insert(3);
	s.insert(5);
	s.insert(2);
	s.insert(5);
	s.insert(6);

	set<int>::iterator it = s.begin();//迭代器遍歷
	while (it != s.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;

 插入如上數據之後,打印出來的值爲1 2 3 4 5 6。set容器自動對以上數據進行了排序,並且實現了去重。但是不能對set裏的值進行修改。 


查找 --->

	set<int>::iterator pos = s.find(3);// --- 時間複雜度:O(log(N)),底層是搜索樹,建議使用
	//pos = find(s.begin(), s.end(), 3);// --- 時間複雜度:O(N),需要遍歷一遍不建議使用
	if (pos != s.end())
	{
		cout << "找到啦!" << endl;
	}

刪除 --->

	s.erase(it);       //該語句在循環外時,有3就刪除,沒有3就報錯
	s.erase(3);       //有3就刪除,沒有3就不刪
	for (auto e : s)
	{
		cout << e <<" ";
	}
	cout << endl;

採用s.erase(3);這種操作如果沒有3並不會報錯,如果有3則會刪除這個結點。

找到pos位置,採用s.erase(pos);這種操作如果沒有3則會報錯,如果有3則會刪除這個結點。


交換 --->

	set<int> s2;
	s2.insert(6);
	s2.insert(9);
	s2.insert(8);
	s2.insert(7);
	s2.insert(10);

	s2.swap(s);//交換根節點的指針,效率高
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;

兩個set的交換的其實是交換結點的指針,效率高。


清空 ---> 

	s.clear();//清掉所有數據
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;

遍歷方法 --->

	//迭代器遍歷
	set<int>::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;

	//新式for循環
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;

特點--->

  1. 與map/multimap不同,map/multimap中存儲的是真正的鍵值對<key, value>,set中只放value,但在底層實際存放的是由<value, value>構成的鍵值對。
  2. set中插入元素時,只需要插入value即可,不需要構造鍵值對
  3. set中的元素不可以重複(因此可以使用set進行去重)。
  4.  使用set的迭代器遍歷set中的元素,可以得到有序序列
  5. set中的元素默認按照小於來比較
  6. set中查找某個元素,時間複雜度爲:$ log_2 n $
  7. set中的元素不允許修改
  8. set中的底層使用二叉搜索樹(紅黑樹)來實現

multiset

基本概念 --->

  1. multiset是按照特定順序存儲元素的容器,其中元素是可以重複的。
  2. 在multiset中,元素的value也會識別它(因爲multiset中本身存儲的就是<value, value>組成的鍵值對,因此value本身就是key,key就是value,類型爲T). multiset元素的值不能在容器中進行修改(因爲元素總是const的),但可以從容器中插入或刪除。
  3. 在內部,multiset中的元素總是按照其內部比較規則(類型比較)所指示的特定嚴格弱排序準則進行排序。
  4. multiset容器通過key訪問單個元素的速度通常比unordered_multiset容器慢,但當使用迭代器遍歷時會得到一個有序序列。
  5. multiset底層結構爲二叉搜索樹(紅黑樹)。

 運用 --->

	multiset<int> ms;
	ms.insert(3);
	ms.insert(4);
	ms.insert(5);
	ms.insert(2);
	ms.insert(1);
	ms.insert(5);
	ms.insert(4);
	ms.insert(3);
	//可以插入重複的相同的key值
	for (auto e : ms)
	{
		cout << e << " ";
	}
	cout << endl;

	auto mit = ms.find(3);
	if (mit != ms.end())//因爲有多個3,所以找到的是中序遍歷的第一個
	{
		cout << "找到啦~!" << endl;
		while (*mit == 3)//繼續遍歷輸出所有的3
		{
			cout << *mit <<" ";
			++mit;
		}
		cout << endl;
	}

multiset的接口和set基本都相同,但是值得注意的是multiset可以插入key相同的值。

multiset允許key的冗餘,如果用find查找key值時,找到的是中序遍歷第一個,因此不斷遍歷下午可以找到這個multiset裏所有的key值。

multiset和set一樣不能夠對數據進行修改。


特點 --->

  1. multiset中再底層中存儲的是<value, value>的鍵值對
  2. mtltiset的插入接口中只需要插入即可
  3. 與set的區別是,multiset中的元素可以重複,set是中value是唯一的
  4. 使用迭代器對multiset中的元素進行遍歷,可以得到有序的序列
  5. multiset中的元素不能修改
  6. 在multiset中找某個元素,時間複雜度:$ O(log_2 N) $
  7. multiset的作用:可以對元素進行排序

 

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