關於C++中Hash的應用
阿新 • • 發佈:2018-11-18
temp color 2種 .get 技術 int names png 默認 ,故可以直接進行使用。可一旦你的類為自定義類型, 其中的hash和equal就得由你自己提供。其實也不難理解, 假設你的對象是一塊石頭,石頭怎麽進行hash, 石頭怎麽怎麽比大小呢?編譯器當然不知道,這就需要你告訴編譯器。下面我們對這2種情況分別舉例說明。
本文只介紹我們在C++中如何使用Hash這種數據結構達到我們編程的目的,有關Hash的概念和實現不做詳談。
C++11新增了一類散列容器包括unordered_set, unordered_map, unordered_multiset, unordered_multimap, 即之前熟悉的hash_set, hash_map等。
這類容器底層以哈希表實現之,通過unordered_map介紹下這類容器的使用。
unordered_map 是一個模板類,需要我們提供5個魔板參數。依次為:key值的類型, value值的類型,hash函數, 等價函數, 容器分配器。其中後三個有默認參數,那我們是不是只需要提供前2個模板參數就可以使用了呢? 不一定。當我們使用的key為內置類型時(如int, double, float, string等),後面三個默認模板參數在STL內有其特化版本
(一)、當key為內置類型:
unordered_map<string, int> m_map;
當key為內置類型, 僅需提供key與value的類型便可運用。 其中hash<string> 與 equal <int> 均有特化版本,分配器對整個容器進行內存管理,這三個參數均為默認參數。
(二)、當key為自定義類型:
比如我們簡單定義一個package類,裏面僅有名字,電話2項數據。
class package { public: string getName() const { return name; } long long getPhone() const { return phone; } package(string m_name = 0, long long m_pNum = 0); bool operator== (const package& p) const { returnname == p.name && phone == p.phone; } private: string name; long long phone; };
然後將原生hash包裝使用下:
namespace std { template<> struct hash<package> { size_t operator() (const package& s) const noexcept { return hash<decltype(s.getName())>()(s.getName()) + hash<decltype(s.getPhone())>()(s.getPhone()); } }; // 間接調用原生Hash. }
或者可以借助借助boost庫的hash_value:
namespace std { template<> struct hash<package> { size_t operator() (const package& s) const noexcept { auto t = make_tuple(s.getName(), s.getPhone()); size_t value = boost::hash_value(t); return value; // make_tuple(s.getName(), s.getPhone()) 等價於 tuple<string, long long>()(s.getName(), s.getPhone()) } }; // 間接調用原生Hash. }
當我們把Hash函數(package的特化版本)和 等價函數 (操作符==重載)提供後, 便可使用自定義版本的unordered_map了:
unordered_map<package, int> m_map;
下面給出測試代碼:
(測試環境: VS2017)
#include <iostream> #include <unordered_map> #include <string> #include <algorithm> //#include <boost/functional/hash.hpp> // 根據安裝路徑選擇hash.hpp #include <tuple> using namespace std; class package { public: string getName() const { return name; } long long getPhone() const { return phone; } package(string m_name = 0, long long m_pNum = 0); bool operator== (const package& p) const { return name == p.name && phone == p.phone; } private: string name; long long phone; }; package::package(string m_name, long long m_pNum) : name(m_name), phone(m_pNum) { } namespace std { template<> struct hash<package> { size_t operator() (const package& s) const noexcept { return hash<decltype(s.getName())>()(s.getName()) + hash<decltype(s.getPhone())>()(s.getPhone()); //auto t = make_tuple(s.getName(), s.getPhone()); //size_t value = boost::hash_value(t); //return value; // make_tuple(s.getName(), s.getPhone()) 等價於 tuple<string, long long>()(s.getName(), s.getPhone()) } }; // 間接調用原生Hash. } int main() { unordered_map<package, int> m_map; package p1{ "Wang", 13399996666}; package p2{ "Li", 13399993333}; package p3{ "Zhang", 13399992222}; package p4{ "Zhou", 13399991111 }; package p5{ "Wang", 13399996666}; package p6{ "Wang", 13366669999 }; m_map[p1]++; m_map[p2]++; m_map[p3]++; m_map[p4]++; m_map[p5]++; m_map[p6]++; cout << m_map.bucket(p1) << " "; cout << m_map.bucket(p2) << " "; cout << m_map.bucket(p3) << " "; cout << m_map.bucket(p4) << " "; cout << m_map.bucket(p5) << " "; cout << m_map.bucket(p6) << " " << endl; return 0; }
關於C++中Hash的應用