編程語言C/C++(七)—— STL(二)

set

實現原理:
set 的特性是,所有元素都會根據元素的鍵值自動被排序。set 的元素不像 map 那樣可以同時擁有實值(value)和鍵值(key),set 元素的鍵值就是實值,實值就是鍵值,set不允許兩個元素有相同的值。
set 底層是通過紅黑樹(RB-tree)來實現的,由於紅黑樹是一種平衡二叉搜索樹,自動排序的效果很不錯,所以標準的 STL 的 set 即以 RB-Tree 爲底層機制。又由於 set 所開放的各種操作接口,RB-tree 也都提供了,所以幾乎所有的 set 操作行爲,都只有轉調用 RB-tree 的操作行爲而已。

用法:
一些用法與上面的類似,這裏就不全部列出來了

	初始化:
		  set<int> s;
		  set<int, mycompare> s;  
		  //mycompare其實就是排序規則,查找等操作全部是按照它來,最好用仿函數吧
		    
	賦值操作:
		  s.swap(s1);  //交換集合s和s1
		  s = s1;  
		    		
	 插入操作:
		  s.insert(10);  //往集合中插入10
		    		
	刪除操作:
		  s.erase(s.begin(), s.end()-1);  //刪除……,返回下一個元素的迭代器

	查找操作:
		  s.find(10);  
		  //查找元素10是否存在,如果存在,返回其迭代器,否則返回s.end()
		  
		  s.lower_bound(10); 
		   //查找第一個大於等於10的迭代器,如果沒有,則返回s.end()
		   
		  s.upper_bound(10); 
		  //查找第一個大於10的迭代器,如果沒有,則返回s.end()
		  
		  s.equal_range(10);  
		  //返回 s.lower_bound(10);和s.upper_bound(10);的迭代器,以對組的方式返回,一樣需要判斷s.end()

對組:
pair<iterator, iterator> pair<int, int>等等等…… 可以 = 賦值,另外也可以使用make_pair函數
EG: pair<set::iterator, set::iterator> ret = s.equal_range(10);
我們可以通過first 以及 second 兩個公有函數來訪問pair的兩個值,即ret.first……

仿函數(函數對象,C++中可以使用這個來取代函數指針,畢竟挺強大的~):
仿函數的主要功能是爲了搭配STL算法使用,單獨使用仿函數的情況比較少。仿函數(functors)在C++標準中採用的名稱是函數對象(function objects)。

仿函數主要用於STL中的算法中,雖然函數指針雖然也可以作爲算法的參數,但是函數指針不能滿足STL對抽象性的要求,也不能滿足軟件積木的要求–函數指針,無法和STL其他組件搭配,產生更靈活變化。仿函數本質就是類重載了一個operator(),創建一個行爲類似函數的對象。

對於重載了()操作符的類,可以實現類似函數調用的過程,所以叫做仿函數,實際上仿函數對象僅僅佔用1字節,因爲內部沒有數據成員,僅僅是一個重載的方法而已。實際上可以通過傳遞函數指針實現類似的功能,但是爲了和STL內部配合使用,他提供了仿函數的特性。

函數對象超出了函數的概念,函數對象可以保存函數調用的狀態:

  • 如果是普通函數且我們想獲取調用次數,那我們可以使用全局變量,但是真正開發中,我們是避免去使用全局變量的,因爲在多線程環境中我們需要去進行加鎖解鎖;

    • 而仿函數(函數對象)是一個類,我們只需在裏面聲明一個變量即可。
    • 像for_each、sort(algorithm)這類函數,我們也可以傳入函數對象(模板的思想)

EG:

			class mycompare
			{
					public:
					bool operator()(int v1, int v2){return v1 > v2;}
			};
			int main(void)
			{
					set<int, mycompare> s;
			}
  • 一元仿函數、二元仿函數……:根據參數的個數來稱呼

  • 謂詞是指普通函數或重載的operator()返回值是bool類型的函數對象(仿函數)。如果operator接受一個參數,那麼叫做一元謂詞,如果接受兩個對象,那麼叫二元謂詞,謂詞可作爲一個判斷式。

multiset

multiset的特性以及用法和 set 完全相同,唯一的差別在於它允許鍵值重複,因此它的插入操作採用的是底層機制是 RB-tree 的 insert_equal()(即大於等於走右邊) 而非 insert_unique()。

map

實現原理:
map的特性是,所有元素都會根據元素的鍵值自動被排序。map 的所有元素都是 pair,同時擁有實值(value)和鍵值(key)。pair 的第一元素被視爲鍵值,第二元素被視爲實值。

map不允許兩個元素擁有相同的鍵值。由於 RB-tree 是一種平衡二叉搜索樹,自動排序的效果很不錯,所以標準的STL map 即以 RB-tree 爲底層機制。又由於 map 所開放的各種操作接口,RB-tree 也都提供了,所以幾乎所有的 map 操作行爲,都只是轉調 RB-tree 的操作行爲。

用法:
map的很多方法和set是類似的。

	初始化:
		map<int, string> m;
	    map<int, string, mycompare> m;  
	    //mycompare其實就是排序規則,查找等操作全部是按照它來,最好用仿函數吧

    插入:
		map<int, string> mapStu;
		mapStu.insert(pair<int, string>(3,"body1")); 
		 //第一種方法,通過pair的方式插入對象
		mapStu.insert(make_pair(1, "body2"));  
		//第二種方法,通過make_pair創建pair的方式插入對象
		mapStu.insert(map<int, string>::value_type(5, "body3")); 
		 //第三種方法,通過value_type的方式插入對象
		mapStu[2] = "body4";  
		//第四種方法,通過數組的方式插入值
		
		//**前三種方法區別不大,用數組方式插入時,如果發現key不存在,則會創建pair並插入到map中,如果存在,那麼會修改其實值**
		//**注意,使用cout << mapStu[6] << endl; 時,如果通過[]方式去訪問一個不存在的key,那麼map會將這個訪問的key插入到map中,且value會採取默認值**
					
    訪問:
	    map<int, string>::iterator it;
		通過it->first 訪問鍵值,通過it->second 訪問實值
		pair<int, string> p;
		通過p.first 訪問鍵值,通過p.second 訪問實值,這裏要和迭代器區分開!

    查找操作:
		map<int, string> m;
		m.find(10); 
		 //查找鍵值10是否存在,如果存在,返回其迭代器,否則返回m.end()
		m.lower_bound(10);  
		//查找第一個大於等於10的迭代器,如果沒有,則返回m.end()
		m.upper_bound(10); 
		//查找第一個大於10的迭代器,如果沒有,則返回m.end()
		m.equal_range(10); 
		 //返回 m.lower_bound(10);和m.upper_bound(10);的迭代器,以對組的方式返回,一樣需要判斷m.end()

multimap

multimap 的特性以及用法與 map 完全相同,唯一的差別在於它允許鍵值重複,因此它的插入操作採用的是底層機制 RB-tree 的 insert_equal() 而非 insert_unique。

注意:
multimap的仿函數使用和set、multiset、map的不一樣!!

				class Mycompare
				{
				public:
				    bool operator()(int v1, int v2)const  //這裏必須加const
				    {
				        return v1>v2;
				    }
				};
				int main(void)
				{
						multimap<int,string, mycompare> m;
				}
				

另外,multimap不支持[],即不能使用s[10]=10這類操作!!

hash_set 與 hash_multiset

hash_set 底層數據結構爲 hash 表,無序,不重複。hash_multiset 底層數據結構爲 hash 表,無序,不重複。(貌似是用拉鍊法)

hash_map 與 hash_multimap

hash_map 底層數據結構爲 hash 表,無序,不重複。hash_multimap 底層數據結構爲 hash 表,無序,不重複。

其它

  • STL容器所提供的都是值(value)寓意,而非引用(reference)寓意,也就是說當我們給容器中插入元素的時候,容器內部實施了拷貝動作,將我們要插入的元素再另行拷貝一份放到容器中,而不是將原數據元素直接放進容器中,也就是說我們提供的元素必須那個被拷貝。

  • 通常STL不會拋出異常,需要使用者傳入正確參數

  • STL容器使用時機:

    • vector:單端數組,可隨機存取(有時候稱爲直接訪問),元素搜尋速度慢,元素安插移除在尾端比較快

    • deque:雙端數組,可隨機存取,元素搜尋速度慢,元素安插移除在頭尾兩端比較快

    • list:雙向鏈表,不可隨機存取,元素搜尋速度非常慢,元素安插移除在任何位置都快

    • set:紅黑樹,不可隨機存取,元素搜尋速度快

    • multiset:紅黑樹,不可隨機存取,元素搜尋速度快

    • map:紅黑樹,對key而言是可隨機存取,對key而言元素搜尋速度快

    • multimap:紅黑樹,不可隨機存取,對key而言元素搜尋速度快

  • 函數對象(仿函數)
    很多東西前面已經講了,這裏就不重複了。

    • STL內建了一些函數對象。分爲:算法類函數對象,關係類函數對象,邏輯類函數對象。這些函數對象所產生的對象,用法和一般的函數完全相同,
      當然我們還可以產生無名的臨時對象來履行函數功能。使用內建函數對象,需要引入頭文件 #include。

      • 函數對象適配器(重點)(functional庫)
        函數對象適配器是完成一些配接工作,這些配接工作包括綁定(bind)、否定(negate),以及對一般函數或成員的修飾,使其成爲函數對象。

        • 綁定適配器:bind1st、bind2nd

          • 功能:將一個二元函數對象轉變爲一元函數對象

          • 區別:

            • bind1st(叫bind first)是將傳進來的參數綁定爲函數對象的第一個參數

            • bind2nd(叫bind second)是將傳進來的參數綁定爲函數對象的第二個參數

            • 例子:

        class Myprint : public binary_function<int, int, void>  
      //注意,使用綁定配接器必須使我們的函數對象繼承自binary_function,
      binary_function是一個模板類,我們需要傳入<參數1類型, 參數二類型, 返回類型>
        {public:
        		void operator()(int v, int val) const   //注意,這裏必須使用const修飾
        		{
        				cout << v + val << endl;
        		}
        }
       ……
        int addNum = 200;
        for_each(v.begin(), v.end(), bind2st(Myprint, addNum));
  • 取反適配器:not1、not2

    • 功能:對謂詞的返回進行取反

    • 區別:not1是對一元謂詞進行取反,而not2是對二元謂詞進行取反

    • 例子:

		class Mycompare : public binary_function<int, int, bool>  
        //注意,使用not2配接器必須使我們的函數對象繼承自binary_function,
        我們這裏是二元的,因此是繼承自binary_function
       {public:
        		bool operator()(int v1, int v2) const   
        		//注意,這裏必須使用const修飾
        		{
        				return v1 > v2;
        		}
       }
        class Mycompare5 : public binary_function<int, bool>  
        //注意,這裏繼承的父類是unary_function
        {public:
        			bool operator()(int v1) const   
        			//注意,這裏必須使用const修飾
        			{
        					return v1 > 5;
        			}
        }
        ……
        sort(v.begin(), v.end(), not2(Mycompare));
        						
        find_if(v.befin(), v.end(), not1(Mycompare5));
  • 關於find_if算法(使用方法參考上面的例子)
    這個算法的功能是返回第一個滿足條件的迭代器

  • 函數對象適配器:ptr_fun

    • 功能:把函數轉變爲函數對象

    • 例子:

		void myPrint(int val1, int val2)
		{
				cout << val1 + val2 << endl;
		}
		……
		for_each(v.begin(), v.end(), bind2st(ptr_fun(myPrint), 10));
  • 成員函數適配器 mem_fun、mem_fun_ref

    • 功能:如果容器中存放的是對象或者對象指針,我們for_each算法打印的時候又想用類自己提供的打印函數

    • 例子

    	class Stu
    	{
    			public: void Print(int val1, int val2);
    	}
    	……
    	vector<Stu> v1;
    	for_each(v1.begin(), v1.end(), mem_fun_ref(&Stu::Print));
    	//注意使用的方法
    	vector<Stu*> v2;
    	for_each(v2.begin(), v2.end(), mem_fun(&Stu::Print));
    	//注意mem_fun和mem_fun_ref的使用,必須加&符號!!
    	//當容器內是對象時,使用mem_fun_ref;當容器內是對象指針時,使用mem_fun。
    
  • 常用的算法

    • 常用的遍歷算法

      • for_each(iterator beg, iterator end, _callback);

      • transform(iterator beg1, iterator end1, iterator beg2, _callback)

        • 將指定容器區間元素搬運到另一個容器中,注意另一個容器的空間必須是初始化過的,可以使用resize,但不可以使用reserve,參考二者的區別
        • _callback是搬運的方法,例子如下:
        			class Myplus
        			{public:
        					int operator()(int val){return val; }
        			}
        			……
        			transform(v1.begin(), v1.end(), v2.begin(), Myplus());
        
    • 常用的查找算法

      • find(iterator beg, iterator, value);

        • 需要注意的是(下面也一樣),自定義對象間的比較要求我們重載==運算符,如bool operator ==(const Person p)const{}
      • adjacent_find(iterator beg, iterator end, _callback);

        • 查找相鄰重複元素,_callback爲回調函數或者謂詞,返回相鄰元素的第一個位置的迭代器
      • bool binary_search(iterator beg, iterator end, value);

        • 二分查找,在無序序列中不能使用,查找成功返回true,否則返回false
      • find_if(iterator beg, iterator end, _callback);

        • 查找第一個滿足條件的元素(_callback,謂詞),並返回其迭代器,如果沒有當然是返回end()
      • count(iterator beg, iterator end, value);

        • 統計元素的出現個數並返回(int)
      • count_if(iterator beg, iterator end, _callback);

        • 統計滿足條件的元素的個數並返回(int)
    • 常用排序算法

      • merge(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest, _callback);

        • 將兩個已排序(必須都是從小到大或從大到小)合併,並存儲到另一個容器中
      • sort(iterator beg, iterator end, _callback);

        • 容器元素排序
      • random_shuffle(iterator beg, iterator end);

        • 對指定範圍內的元素隨機調整次序
      • reverse(iterator beg, iterator end);

        • 反轉指定範圍的元素
    • 常用拷貝和替換算法

      • copy(iterator beg, iterator end, iterator dest);

        • 將容器內指定範圍的元素拷貝到另一容器中
      • replace(iterator beg, iterator end, oldvalue, newvalue);

        • 將容器內指定範圍的舊元素修改爲新元素
      • replace_if(iterator beg, iterator end, _callback, newvalue);

        • 將容器內指定範圍滿足條件的元素替換爲新元素
      • swap(container c1, container c2);
        - 互換兩個容器的元素

    • 常用算術生成算法

      • accumulate(iterator beg, iterator end, value);

        • 計算容器中=元素累計總和+value
      • fill(iterator beg, iterator end, value);

        • 向容器中添加元素
    • 常用的集合算法

      • set_intersection(iterator beg1, iterator end1, iterator iterator beg2, iterator end2, iterator dest);

        • 求兩個集合的交集,返回一個迭代器,該迭代器指向最後一個元素的下一個元素
      • set_union(iterator beg1, iterator end1, iterator iterator beg2, iterator end2, iterator dest);

        • 求兩個集合的並集,返回一個迭代器,該迭代器指向最後一個元素的下一個元素
      • set_difference(iterator beg1, iterator end1, iterator iterator beg2, iterator end2, iterator dest);

        • 求兩個集合的差集,返回一個迭代器,該迭代器指向最後一個元素的下一個元素
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章