【散列】杜鹃散列详情与C++实现代码
生活随笔
收集整理的這篇文章主要介紹了
【散列】杜鹃散列详情与C++实现代码
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
導引
在球-箱問題中,如果將N項隨機拋入N個箱子中,那么含球最多的箱子的期望球數為Θ(logN/log logN)。
如果在每次投擲中隨機選取兩個箱子且將被投項投入(在那一刻)較空的箱子中,則最大箱子的球數只是θ(log logN),這是一個顯著更小的數。其中一種做法就是杜鵑散列(cuckoo hashing)。
概念
在杜鵑散列中,假設我們有N項,我們保持兩個散列表,每個都多于半空,并且我們有兩個獨立的散列函數,它們可將每一項分配給每個表中的一個位置。
杜鵑散列保持下述不變性:一項總是被存儲在它的兩個位置之一中。
杜鵑散列的好處包括最壞情形常數查找和刪除次數,避免懶惰刪除和額外的數據,以及并行處理的可能。但杜鵑散列對散列函數的選擇非常敏感,最后,推薦使用較小的裝填因子或多于兩個的散列函數。
實現
杜鵑散列表常常作為擁有兩個(或更多的)散列函數的一個大表來實現,這些散列函數探測整個大表。如果存在一個可用的位置,那么一些變化的做法則是嘗試把一項立即置入二級散列表中,而不是一開始的位置替換。
杜鵑散列算法本身很簡單:要想插入新項x,首先確認它不在表中。然后使用第一個散列函數,而如果這(第一)個表位置是空的,則該項即可置入。
代碼
//為杜鵑散列生成泛型HashFamily接口,用來發出多簇散列函數到杜鵑散列表 template<typename AnyType> class CuckooHashFamily { public:size_t hash(const AnyType& x, int which)const;int getNumberOfFunctions();void generateNewFunctions(); };/** * 杜鵑散列法的非正式字符串散列 */ template<int count> class StringHashFamily { private:std::vector<int> MULTIPLIERS;UniformRandom r;public:StringHashFamily() :MULTIPLIERS(count) {generateNewFuntions();}int getNumberOfFunctions()const {return count;}void generateNewFuntions() {for (auto& mult : MULTIPLIERS)mult = r.nextInt();}size_t hash(const string& x, int which)const {const int multiplier = MULTIPLIERS[which];size_t hashVal = 0;for (auto ch : x)hashVal = multiplier * hashVal + ch;return hashVal;} };//杜鵑散列類接口,允許(由HashFamily模板參數類型指定)任意個數的散列函數 template<typename AnyType, typename HashFamily> class HashTable { private:struct HashEntry {AnyType element;bool isActive;HashEntry(const AnyType&e=AnyType(),bool a=false):element{e},isActive{a}{}HashEntry(AnyType&&e,bool a=false):element{std::move(e)},isActive{a}{}};/*** 杜鵑散列的插入例程使用不同的算法,* 該算法隨機選擇要逐出的項,* 但不再試圖重新逐出最后的項。* 如果存在太多的逐出項則散列表將嘗試選取新的散列函數(再散列),* 而若有太多的再散列則散列表將擴張*/bool insertHelper1(const AnyType& xx) {const int COUNT_LIMIT = 100;AnyType x = xx;while (true) {int lastPos = -1;int pos;for (int count = 0; count < COUNT_LIMIT; ++count) {for (int i = 0; i < numHashFunctions; ++i)pos = myhash(x, i);if (!isActive(pos)) {array[pos] = std::move(HashEntry{ std::move(x),true });++currentSize;return true;}}//無可用位置,逐出一個隨機項int i = 0;do {pos = myhash(x, r.nextInt(numHashFunctions));} while (pos == lastPos && i++ < 5);lastPos = pos;std::swap(x, array[pos].element);}if (++rehashes > ALLOWED_REHASHES) {expand(); //使散列表擴大rehashes = 0; //重置rehashes的計數}elserehash(); //表大小相同,散列函數都是新的}bool insertHelper1(AnyType&& x) {const int COUNT_LIMIT = 100;while (true) {int lastPos = -1;int pos;for (int count = 0; count < COUNT_LIMIT; ++count) {for (int i = 0; i < numHashFunctions; ++i)pos = myhash(x, i);if (!isActive(pos)) {array[pos] = std::move(HashEntry{ std::move(x),true });++currentSize;return true;}}//無可用位置,逐出一個隨機項int i = 0;do {pos = myhash(x, r.nextInt(numHashFunctions));} while (pos == lastPos && i++ < 5);lastPos = pos;std::swap(x, array[pos].element);}if (++rehashes > ALLOWED_REHASHES) {expand(); //使散列表擴大rehashes = 0; //重置rehashes的計數}elserehash(); //表大小相同,散列函數都是新的}bool isActive(int currentPos)const {return currentPos != -1 && array[currentPos].isActive;}/*** 使用特定函數計算x的散列代碼* 選取適當的散列函數,然后把它換算成合法的數組下標*/size_t myhash(const AnyType& x, int which)const {return hashFunctions.hash(x, which) % array.size();}/*** 查找所有散列函數的位置* 返回查閱所有的散列函數以返回包含項x的下標,若找不到則返回-1*/int findPos(const AnyType& x)const {for (int i = 0; i < numHashFunctions; ++i) {int pos = myhash(x, i);if (isActive(pos) && array[pos].element == x)return pos;}return -1;}/*** 創建一個大數組但使用那些相同的散列函數*/void expand() {rehash(static_cast<int>(array.size() / MAX_LOAD));}/*** 保留數組的大小不變,創建一個新的數組* 該數組使用那些新選出的散列函數填充*/void rehash() {hashFunctions.generateNewFuntions();rehash(array.size());}void rehash(int newSize) {std::vector<HashEntry> oldArray = array;//創建新的雙倍大小的空散列表array.resize(nextPrime(newSize));for (auto& entry : array)entry.isActive = false;//復制整個表currentSize = 0;for (auto& entry : oldArray)if (entry.isActive)insert(std::move(entry.element));}constexpr static const double MAX_LOAD=0.4; //最大裝填因子static const int ALLOWED_REHASHES = 5; //最大散列次數vector<HashEntry>array;int currentSize;int numHashFunctions;int rehashes;UniformRandom r;HashFamily hashFunctions;public:explicit HashTable(int size = 101) :array(nextPrime(size)) {numHashFunctions = hashFunctions.getNumberOfFunctions();rehashes = 0;makeEmpty();}//清空杜鵑散列表void makeEmpty() {currentSize = 0;for (auto& entry : array)entry.isActive = false;}/*** 搜索杜鵑散列表的例程* 如果找到x則返回true*/bool contains(const AnyType& x)const {return findPos(x) != -1;}/*** 從散列表中刪除x* 若項x被找到且被刪除則返回true*/bool remove(const AnyType& x) {int currentPos = findPos(x);if (!isActive(currentPos))return false;array[currentPos].isActive = false;--currentSize;return true;}//杜鵑散列表中公有插入方法bool insert(const AnyType& x) {if (contains(x))return false;if (currentSize >= array.size() * MAX_LOAD)expand(); return insertHelper1(x);}bool insert(AnyType&& x) {if (contains(x))return false;if (currentSize >= array.size() * MAX_LOAD)expand(); return insertHelper1(std::move(x));}int size() const{return currentSize;}int capacity() const{return array.size();} };總結
以上是生活随笔為你收集整理的【散列】杜鹃散列详情与C++实现代码的全部內容,希望文章能夠幫你解決所遇到的問題。