例說資料結構&STL(十)——hash_set/unordered_set
1 白話hash_set/unordered_set
這一章節,我們來了解兩個新的結構體hash_set和unorderd_set。我將這兩者放在一個博文中介紹是因為它們都屬於基於雜湊表(hash table)構建的資料結構,並且是關鍵字與鍵值相等的關聯容器。後面我們還會介紹到hash_map與unordered_map兩種資料結構,這就好比是set與map的區別了,後面我們再說。
說到這那到底hash_set與unordered_set哪個更好呢?實際上unordered_set在C++11的時候被引入標準庫了,而hash_set並沒有,所以建議還是使用unordered_set比較好,這就好比一個是官方認證的,一個是民間流傳的。在編譯器中,Visual Studio(當然需要支援C++11的版本)庫中兩個資料結構都有定義,而在gcc/g++中並不支援hash_set。總之,如果想使用這種基於雜湊表的關聯容器,那麼就使用unordered_set就好了。下面我們也將會圍繞無序集合容器(unordered_set)講解,hash_set有對應的公共介面不再細說。
此外我們來看看unordered_set/hash_set與set有什麼區別。首先從內部構建來看,雖然都屬於關鍵字與鍵值相等的關聯容器,但是內部結構大大的不同。set的內部結構是基於紅黑樹來實現的,所以保證了一個穩定的動態操作時間,查詢、插入、刪除都是O(logN),最壞和平均都是。而unordered_map如前所述,是雜湊表。順便提一下,雜湊表的查詢時間雖然是O(1),但是並不是unordered_map查詢時間一定比map短,因為實際情況中還要考慮到資料量,而且unordered_map的hash函式的構造速度也沒那麼快,所以不能一概而論,應該具體情況具體分析。
第二點從儲存方式來看,unordered_set也是一個儲存唯一(unique,即無重複)的關聯容器(Associative container),但是容器中的元素無特別的秩序關係,該容器允許基於值的快速元素檢索,同時也支援正向迭代。在一個unordered_set內部,元素不會按任何順序排序,而是通過元素值的hash值將元素分組放置到各個桶中,這樣就能通過元素值快速訪問各個對應的元素(均攤耗時為O(1))。
2 小談雜湊表
hash_set/unordered_set是雜湊表構建的,所以我們在介紹其方法介面前還是有進一步瞭解一下雜湊表的原理。
雜湊表是根據關鍵碼值而進行直接訪問的資料結構,通過相應的雜湊函式(也稱雜湊函式)處理關鍵字得到相應的關鍵碼值,關鍵碼值對應著一個特定位置,用該位置來存取相應的資訊,這樣就能以較快的速度獲取關鍵字的資訊。
比如:現有公司員工的個人資訊(包括年齡),需要查詢某個年齡的員工個數。由於人的年齡範圍大約在[0,200],所以可以開一個200大小的陣列,然後通過雜湊函式得到key對應的key-value,這樣就能完成統計某個年齡的員工個數。而在這個例子中,也存在這樣一個問題,兩個員工的年齡相同,但其他資訊(如:名字、身份證)不同,通過前面說的雜湊函式,會發現其都位於陣列的相同位置,這裡,就涉及到“衝突”。準確來說,衝突是不可避免的,而解決衝突的方法常見的有:開發地址法、再雜湊法、鏈地址法(也稱拉鍊法)。而unordered_set內部解決衝突採用的是鏈地址法,當用衝突發生時把具有同一關鍵碼的資料組成一個連結串列。下圖展示了鏈地址法的使用:
3 unordered_set實戰
3.1 標頭檔案
#include<unordered_set> // hash_set則是#iunclude<hash_set>
using namespace std;
3.2 其他操作
由於其常用方法介面和set幾乎一樣,我不在過多描述,下面只貼出程式樣例,一些說明請閱讀博文例說資料結構&STL(八)——set.
unordered_set<int> set_fir; // 預設構造物件
unordered_set<int> set_sed = { 2, 3, 10, 5, 9 }; //初始化構造
set_sed.insert(7); // 插入7,放置在set中位置跟hash構建有關,並不是在尾部
unordered_set<int>::iterator iter1 = set_sed.lower_bound(2); //返回set中>=2的索引(迭代器),切記非小於2
unordered_set<int>::iterator iter2 = set_sed.upper_bound(2); //返回set中>2的索引
set_sed.erase(2); //刪除set中元素2
set_sed.erase(set_sed.begin(), set_sed.end()); //清空整個set
if (set_sed.find(5) != set_sed.end()) // 查詢鍵值為5的元素
cout << "exsit" << endl;
// 正向訪問
unordered_set<int>::iterator iter4;
for (iter4 = set_sed.begin(); iter4 != set_sed.end(); iter4++)
cout << *iter4 << endl;
unordered_set<int>::reverse_iterator iter5; //對應反向迭代器物件
// 反向訪問
for (iter5 = set_sed.rbegin(); iter5 != set_sed.rend(); iter5++)
cout << *iter5 << endl;
set_sed.count(12); // 返回set中元素的個數,由於set的特殊性,所以結果只有0或者1
set_sed.swap(set_fir); // 交換所有資料,需要確保set中元素型別相同
set_sed.clear(); // 清空集合set_sed
set_sed.size(); // 統計set_sed中元素個數
set_sed.empty(); // 判斷set中是否為空,空則返回1
4 小結
上面介紹了無序集合容器資料結構特點以及公用的介面。由於集合是基於雜湊表構建的資料結構,所以其查詢的時間複雜度只有O(1),n為集合中元素的個數。
以上是個人學習記錄,由於能力和時間有限,如果有錯誤望讀者糾正,謝謝!
轉載請註明出處:http://blog.csdn.net/FX677588/article/details/76400389
參考文獻:
http://www.cnblogs.com/davidgu/p/4998083.html
http://blog.csdn.net/vevenlcf/article/details/51743058
http://blog.csdn.net/sdnu111111111/article/details/38658929
相關文章
- 資料結構:線性表-例題資料結構
- 傳說中的資料結構_JAVA資料結構Java
- 【PG結構】Postgresql資料庫資料目錄說明SQL資料庫
- 從一條資料說起——InnoDB儲存資料結構資料結構
- 說說你對資料結構的理解?有哪些?區別?資料結構
- 結構化資料、半結構化資料和非結構化資料
- 2. STL容器結構與分類
- 【資料結構篇】認識資料結構資料結構
- 【資料結構與演算法】通俗易懂說連結串列資料結構演算法
- 侯捷C++ STL體系結構與原始碼剖析:關於moveable的說明C++原始碼
- 資料結構小白系列之資料結構概述資料結構
- 資料結構與演算法——十個排序演算法資料結構演算法排序
- 資料結構資料結構
- Redis 5種資料結構及其使用場景舉例--STRINGRedis資料結構
- 【學習】體系結構-001-例項與資料庫資料庫
- 資料結構與演算法-資料結構(棧)資料結構演算法
- 【PHP資料結構】PHP資料結構及演算法總結PHP資料結構演算法
- leetcode演算法資料結構題解---資料結構LeetCode演算法資料結構
- 資料結構:用例項分析ArrayList與LinkedList的讀寫效能資料結構
- 【資料結構與演算法】Trie(字首樹)模板和例題資料結構演算法
- 資料結構——樹資料結構
- Redis資料結構Redis資料結構
- 資料結構-樹資料結構
- 資料結構-Tree資料結構
- Python資料結構Python資料結構
- 資料結構-堆資料結構
- 資料結構-集合資料結構
- 資料結構 - 字串資料結構字串
- 資料結構---串資料結構
- 資料結構-棧資料結構
- Map 資料結構資料結構
- 【模板】資料結構資料結構
- 模板 - 資料結構資料結構
- 14 資料結構資料結構
- 資料結構 - 堆資料結構
- [資料結構]堆資料結構
- 資料結構 - 圖資料結構
- 資料結構 - 棧資料結構
- 資料結構——列表資料結構