STL系列之六 set與hash set
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的更多函式請查閱
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);
else
printf("%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;
//insert
printf("-----插入資料-----------\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);
//find
printf("-----查詢資料-----------\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
再分享一下我老師大神的人工智慧教程吧。零基礎!通俗易懂!風趣幽默!還帶黃段子!希望你也加入到我們人工智慧的隊伍中來!https://www.cnblogs.com/captainbed