STL系列之六 set與hash_set

來源:互聯網
上載者:User

STL系列之六 set與hash_set

set和hash_set是STL中比較重要的容器,有必要對其進行深入瞭解。在STL中,set是以紅/黑樹狀結構(RB-tree)作為底層資料結構的,hash_set是以Hash table(雜湊表)作為底層資料結構的。set可以在時間複雜度為O(logN)情況下插入、刪除和尋找資料。hash_set操作的時間複雜度則比較複雜,這取決於雜湊函數和雜湊表的負載情況。下面列出set和hash_set的常用函數:

 

set和hase_set的更多函數請查閱MSDN。

 

set的使用範例如下(hash_set類似):

// by MoreWindows( http://blog.csdn.net/MoreWindows )#include <set>#include <ctime>#include <cstdio>using namespace std;int main(){printf("--set使用 by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");const int MAXN = 15;int a[MAXN];int i;srand(time(NULL));for (i = 0; i < MAXN; ++i)a[i] = rand() % (MAXN * 2);set<int> iset;   set<int>::iterator pos; //插入資料 insert()有三種重載iset.insert(a, a + MAXN);//當前集合中個數 最大容納資料量printf("當前集合中個數: %d     最大容納資料量: %d\n", iset.size(), iset.max_size());//依次輸出printf("依次輸出集合中所有元素-------\n");for (pos = iset.begin(); pos != iset.end(); ++pos)printf("%d ", *pos);putchar('\n');//尋找int findNum = MAXN;printf("尋找 %d是否存在-----------------------\n", findNum);pos = iset.find(findNum);if (pos != iset.end())printf("%d 存在\n", findNum);elseprintf("%d 不存在\n", findNum);//在最後位置插入資料,如果給定的位置不正確,會重新找個正確的位置並返回該位置pos  = iset.insert(--iset.end(), MAXN * 2); printf("已經插入%d\n", *pos);//刪除iset.erase(MAXN);printf("已經刪除%d\n", MAXN);//依次輸出printf("依次輸出集合中所有元素-------\n");for (pos = iset.begin(); pos != iset.end(); ++pos)printf("%d ", *pos);putchar('\n');return 0;}

運行結果如下:

 

下面試下在set中使用類(結構體也可以類似這樣做)。這個類很簡單,只有一個成員變數,及設定和擷取這個成員變數的成員函數。

//在set中使用類要重載‘<’並實現拷貝建構函式// by MoreWindows( http://blog.csdn.net/MoreWindows )#include <set>#include <ctime>#include <cstdio>using namespace std;class Node{public:Node(int nAge = 0){m_nAge = nAge;}Node(const Node &na)  //拷貝建構函式{m_nAge = na.GetAge();}int GetAge(){return m_nAge;}private:int m_nAge;};//不能寫成類的成員函數inline bool operator < (const Node &na, const Node &nb) {return na.GetAge() < nb.GetAge();}int main(){int i;set<Node> nset;for (i = 0; i < MAXN; ++i)nset.insert(Node(i));return 0;}

編譯,直接報了3個錯誤!!1個在拷貝建構函式,2個在operator<()函數。如所示:

3個錯誤都是一樣的:

error C2662: “Node::GetAge”: 不能將“this”指標從“const Node”轉換為“Node &” 轉換丟失限定符

這是怎麼回事呀?分析下,拷貝建構函式與operator<()函數出錯,錯誤都指向了GetAge()函數,有點古怪,比較下它們與GetAge()函數,可以發現最大的不同點在於這2個函數都用到了const而GetAge()函數沒有使用const。難道是這個導致報錯了嗎?先給GetAge()函數加個const看看,如下:

       int GetAge()  const //增加這個const

       {

              returnm_nAge;

       }

再編譯,不報錯了。再查下資料,原因如下——因為那2個函數都使用了const修飾的對象,但GetAge()沒有加上const以保證它不修改對象,編譯器認為這種寫法是不安全的,所以就毫不猶豫報了個錯誤。

這種錯誤如果不親身體會下,到筆試面試時很可能寫了個錯誤程式而自己還處於一無所知中(死在這些小細節上最不值得)。另外,如果使用VC6.0則不會提示詳細的錯誤資訊——“轉換丟失限定符”。

 

STL還為set提供了一些集合運算的函數,如交集set_intersection()、並集set_union()、差集set_difference()和對稱差集set_symmetric_difference()。這些就不詳細介紹了,有興趣可以自己動手試一試。

下面開始對set和hash_set作個效能測試(Win7 +VS2008Release下)。

測試代碼如下:

// by MoreWindows( http://blog.csdn.net/MoreWindows )#include <set>#include <hash_set>#include <iostream>#include <ctime>#include <cstdio>#include <cstdlib>using namespace std;using namespace stdext;  //hash_set// MAXN個資料 MAXQUERY次查詢const int MAXN = 10000, MAXQUERY = 5000000;int a[MAXN], query[MAXQUERY];void PrintfContainertElapseTime(char *pszContainerName, char *pszOperator, long lElapsetime){printf("%s 的%s操作 用時 %d毫秒\n", pszContainerName, pszOperator, lElapsetime);}int main(){printf("set VS hash_set 效能測試 資料容量 %d個 查詢次數 %d次\n", MAXN, MAXQUERY);const int MAXNUM = MAXN * 4;const int MAXQUERYNUM = MAXN * 4;printf("容器中資料範圍 [0, %d) 查詢資料範圍[0, %d)\n", MAXNUM, MAXQUERYNUM);printf("--by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");//隨機產生在[0, MAXNUM)範圍內的MAXN個數int i;srand(time(NULL));for (i = 0; i < MAXN; ++i)a[i] = (rand() * rand()) % MAXNUM;//隨機產生在[0, MAXQUERYNUM)範圍內的MAXQUERY個數srand(time(NULL));for (i = 0; i < MAXQUERY; ++i)query[i] = (rand() * rand()) % MAXQUERYNUM;set<int>       nset;hash_set<int> nhashset;clock_t  clockBegin, clockEnd;//insertprintf("-----插入資料-----------\n");clockBegin = clock();  nset.insert(a, a + MAXN); clockEnd = clock();printf("set中有資料%d個\n", nset.size());PrintfContainertElapseTime("set", "insert", clockEnd - clockBegin);clockBegin = clock();  nhashset.insert(a, a + MAXN); clockEnd = clock();printf("hash_set中有資料%d個\n", nhashset.size());PrintfContainertElapseTime("hase_set", "insert", clockEnd - clockBegin);//findprintf("-----查詢資料-----------\n");int nFindSucceedCount, nFindFailedCount; nFindSucceedCount = nFindFailedCount = 0;clockBegin = clock(); for (i = 0; i < MAXQUERY; ++i)if (nset.find(query[i]) != nset.end())++nFindSucceedCount;else++nFindFailedCount;clockEnd = clock();PrintfContainertElapseTime("set", "find", clockEnd - clockBegin);printf("查詢成功次數: %d    查詢失敗次數: %d\n", nFindSucceedCount, nFindFailedCount);nFindSucceedCount = nFindFailedCount = 0;clockBegin = clock();  for (i = 0; i < MAXQUERY; ++i)if (nhashset.find(query[i]) != nhashset.end())++nFindSucceedCount;else++nFindFailedCount;clockEnd = clock();PrintfContainertElapseTime("hash_set", "find", clockEnd - clockBegin);printf("查詢成功次數: %d    查詢失敗次數: %d\n", nFindSucceedCount, nFindFailedCount);return 0;}

在資料容量100萬,查詢次數500萬時,程式運行結果如下:

由於查詢的失敗次數太多,這次將查詢範圍變小使用再測試下:

由於結點過多,80多萬個結點,set的紅/黑樹狀結構樹高約為19(2^19=524288,2^20=1048576),查詢起來還是比較費時的。hash_set在時間效能上比set要好一些,並且如果查詢成功的幾率比較大的話,hash_set會有更好的表現。想知道為什麼hash_set會有優良的效能表現,請看繼集——《STL系列之九 探索hash_set》。

 

 

注1.   MSDN上講set的erase()是有傳回值的,但在VS2008中查看set的原始碼,erase()函數的三個重載版本中,有二個傳回值都為void即無傳回值,另一個返回size_type。 可以通過http://msdn.microsoft.com/zh-cn/library/8h4a3515(v=VS.90).aspx查看MSDN上對set的erase()說明。

 

 

轉載請標明出處,原文地址:http://blog.csdn.net/morewindows/article/details/7029587

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.