模仿stl,实现了hashtable。纯属练手,只实现其基本功能,不当之处还望指正。本文为实现独立的空间配置器。
#include<iostream> #include<vector> #include<algorithm> using namespace std; template<class value> struct _hash_node{ value val; _hash_node *next; ~_hash_node(){delete val;} }; template<class value,class key,class HashFcn,class EqualKey> class _hashtable; template<class T1,class T2> class _hashfcn_mod; template<class value,class key,class HashFcn,class EqualKey> class _hashtable_iterator{ public: typedef _hashtable<value,key,HashFcn,EqualKey> hashtable; typedef _hashtable_iterator<value,key,HashFcn,EqualKey> iterator; typedef _hash_node<value> node; typedef forward_iterator_tag iterator_category; typedef value value_type; typedef ptrdiff_t difference_type; typedef size_t size_type; typedef value& reference; typedef value* pointer; node* cur; hashtable* ht; iterator(node* n,hashtable* tab):cur(n),ht(tab){} iterator(){} reference operator*()const{return cur->val;} pointer operator->()const{return &(operator*()); } iterator& operator++(){ const node* old = cur; cur = cur->next; //需要判断cur的next是否存在 if(!cur){ //若此bucket list已经遍历到null 则继续向下一个bucket移动 size_type bucket = ht->bkt_num(old->val); while(!cur&& ++bucket < ht->buckets.size()) cur = ht->buckets[bucket]; } return *this; } iterator operator++(int){ const iterator old = cur; ++*this; return old; } bool operator==(const iterator& it)const{return cur == it.cur;} bool operator!=(const iterator& it)const{return cur != it.cur;} }; static const int _stl_num_primes = 28; //保存28个质数来设计表格大小 static const unsigned long _stl_prime_list[_stl_num_primes] = { 53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593, 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741, 3221225473ul, 4294967291ul }; //获取大于等于n的第一个质数 inline unsigned long _stl_next_prime(unsigned long n){ const unsigned long* first = _stl_prime_list; const unsigned long* last = _stl_prime_list + _stl_num_primes; const unsigned long* pos = lower_bound(first,last,n); return pos == last? *(last-1):*pos; } template<class value,class key,class HashFcn,class EqualKey> class _hashtable{ public: typedef HashFcn hasher; typedef EqualKey key_equal; typedef value value_type; typedef key key_type; typedef value_type& reference; typedef size_t size_type; typedef _hash_node<value> node; typedef _hashtable_iterator<value,key,HashFcn,EqualKey> iterator; vector<node*> buckets; private: hasher hash; //哈希映射函数 key_equal equals; size_type num_elements; private: void initialize_buckets(size_type n){ const size_type n_buckets = next_size(n); buckets.reserve(n_buckets); buckets.insert(buckets.end(), n_buckets, (node*) 0); num_elements = 0; } size_type next_size(size_type n)const{return _stl_next_prime(n);} void copy_from(const _hashtable& ht) { buckets.clear(); buckets.reserve(ht.buckets.size()); buckets.insert(buckets.end(), ht.buckets.size(), (node*) 0); try { for (size_type i = 0; i < ht.buckets.size(); ++i) { if (const node* cur = ht.buckets[i]) { node* copy = new_node(cur->val); buckets[i] = copy; for (node* next = cur->next; next; cur = next, next = cur->next) { copy->next = new_node(next->val); copy = copy->next; } } } num_elements = ht.num_elements; } catch(...){ clear(); } } node* new_node(const value_type& obj) { node* n = allocate((node*)0); n->next = 0; try { construct(&n->val, obj); return n; } catch(...){ deallocate(n); exit(1); } } template<class T> T* allocate(T* a,ptrdiff_t size=1){ set_new_handler(0); T* tmp = (T*)(::operator new((size_t)(size*sizeof(T)))); if(tmp == 0){ cerr<<"out of memory."<<endl; exit(1); } return tmp; } template<class T1,class T2> void construct(T1* p,const T2& value){new (p)T1(value);} template<class T> void deallocate(T* buffer){::operator delete(buffer);} void clear(){ for (size_type i = 0; i < buckets.size(); ++i) { node* cur = buckets[i]; while (cur != 0) { node* next = cur->next; delete_node(cur); cur = next; } buckets[i] = 0; } num_elements = 0; } void delete_node(node* n) { destroy(&n->val); deallocate(n); } template <class T> void destroy(T* pointer) { pointer->~T(); } size_type bkt_num_key(const key_type& key) { return bkt_num_key(key, buckets.size()); } size_type bkt_num_key(const key_type& key, size_t n) { return hash(key,n);// % n; } iterator insert_equal_noresize(const value_type& obj){ size_type n = bkt_num(obj); node* first = buckets[n]; for(node* cur=first;cur;cur=cur->next){ if(equals(get_key(cur->val),get_key(obj))){ node* tmp = new_node(obj); tmp->next = cur->next; cur->next = tmp; ++num_elements; returniterator(tmp,this); } } node* tmp = new_node(obj); tmp->next = first; buckets[n] = tmp; return iterator(tmp,this); } pair<iterator, bool> insert_unique_noresize(const value_type& obj){ const size_type n = bkt_num(obj); node* first = buckets[n]; for (node* cur = first; cur; cur = cur->next) if (equals(get_key(cur->val), get_key(obj))) return pair<iterator, bool>(iterator(cur, this), false); node* tmp = new_node(obj); tmp->next = first; buckets[n] = tmp; ++num_elements; return pair<iterator, bool>(iterator(tmp, this), true); } value_type get_key(const value_type& obj){ ExtractKey<value_type> tmp; return tmp(obj); } public: size_type bucket_size(){return buckets.size();} _hashtable(size_type n,const HashFcn& hf,const EqualKey& eql):hash(hf),equals(eql){ initialize_buckets(n); } _hashtable(const _hashtable& ht):hash(ht.hash),equals(ht.equals),num_elements(0){ copy_from(ht); } _hashtable(){clear();} size_type bucket_count()const{return buckets.size();} size_type max_bucket_count()const{return _stl_prime_list[_stl_num_primes - 1];} size_type elems_in_bucket(size_type bucket)const{ size_type result = 0; for(*node cur = buckets[bucket];cur;cur = cur->next) result += 1; return result; } size_type bkt_num(const value_type& obj) { return bkt_num_key(get_key(obj)); } size_type bkt_num(const value_type& obj, size_t n) const { return bkt_num_key(get_key(obj), n); } void resize(size_type num_elements_hint){ _hashfcn_mod<value_type,value_type> hashfcn_mod; const size_type old_n = buckets.size(); if(num_elements_hint > old_n){ const size_type n = next_size(num_elements_hint); if(n > old_n){ vector<node*> tmp(n,(node*)0); try{ for(size_type bucket=0;bucket<old_n;++bucket){ node* first = buckets[bucket]; while(first){ size_type new_bucket = hashfcn_mod(first->val,n); buckets[bucket] = first->next; first->next = tmp[new_bucket]; tmp[new_bucket] = first; first = buckets[bucket]; } } buckets.swap(tmp); } catch(...){ for(size_type bucket=0;bucket<tmp.size();++bucket){ while(tmp[bucket]){ node* next = tmp[bucket]->next; delete_node(tmp[bucket]); tmp[bucket] = next; } } throw; } } } } pair<iterator,bool> insert_unique(const value_type& obj){ resize(num_elements+1); return insert_unique_noresize(obj); } iterator insert_equal(const value_type& obj) { resize(num_elements + 1); return insert_equal_noresize(obj); } iterator begin() { for (size_type n = 0; n < buckets.size(); ++n) if (buckets[n]) return iterator(buckets[n], this); return end(); } iterator end() { return iterator(0, this); } size_type erase(const key_type& key) { const size_type n = bkt_num_key(key); node* first = buckets[n]; size_type erased = 0; if (first) { node* cur = first; node* next = cur->next; while (next) { if (equals(get_key(next->val), key)) { cur->next = next->next; delete_node(next); next = cur->next; ++erased; --num_elements; } else { cur = next; next = cur->next; } } if (equals(get_key(first->val), key)) { buckets[n] = first->next; delete_node(first); ++erased; --num_elements; } } return erased; } void erase(const iterator& it) { if (node* const p = it.cur) { const size_type n = bkt_num(p->val); node* cur = buckets[n]; if (cur == p) { buckets[n] = cur->next; delete_node(cur); --num_elements; } else { node* next = cur->next; while (next) { if (next == p) { cur->next = next->next; delete_node(next); --num_elements; break; } else { cur = next; next = cur->next; } } } } } reference find_or_insert(const value_type& obj){ resize(num_elements + 1); size_type n = bkt_num(obj); node* first = buckets[n]; for (node* cur = first; cur; cur = cur->next) if (equals(get_key(cur->val), get_key(obj))) return cur->val; node* tmp = new_node(obj); tmp->next = first; buckets[n] = tmp; ++num_elements; return tmp->val; } iterator find(const key_type& key){ size_type n = bkt_num_key(key); node* first; for ( first = buckets[n];first && !equals(get_key(first->val), key); first = first->next); return iterator(first, this); } void erase(iterator first, iterator last){ size_type f_bucket = first.cur ? bkt_num(first.cur->val) : buckets.size(); size_type l_bucket = last.cur ? bkt_num(last.cur->val) : buckets.size(); if (first.cur == last.cur)return; else if (f_bucket == l_bucket)erase_bucket(f_bucket, first.cur, last.cur); else { erase_bucket(f_bucket, first.cur, 0); for (size_type n = f_bucket + 1; n < l_bucket; ++n) erase_bucket(n, 0); if (l_bucket != buckets.size()) erase_bucket(l_bucket, last.cur); } } }; /***************************************************** 此函数对象用于定义映射函数,根据自己的需求可以定义线性探测、 二次线性探测或者自定义函数 ***********************************************************/ template<class T1,class T2> class _hashfcn_mod{ //简单取余映射 public: T1 operator()(T1 value,T2 size){ //value为key值 size为bucket长度 return value % size; } }; /************************************************************* 判断键值是否相等的函数,可自定义 *************************************************************/ template<class T> class _key_equal{ public: bool operator()(T t1,T t2){ return t1 == t2; } }; /************************************************************ 从节点中取出键值的方法,可自定义 ***********************************************************/ template<class T> class ExtractKey{ //从节点取出键值 public: T operator()(const T& tmp){ identity<T> id; return id(tmp); } }; void test1(){ _hashfcn_mod<int,int> hashfcn; _key_equal<int> keyequal; _hashtable<int,int,_hashfcn_mod<int,int>,_key_equal<int>> hashtab(20,hashfcn,keyequal); hashtab.insert_unique(15); hashtab.insert_unique(14); hashtab.insert_unique(13); hashtab.insert_unique(12); _hashtable<int,int,_hashfcn_mod<int,int>,_key_equal<int>>::iterator iter; for(iter = hashtab.begin();iter!= hashtab.end();++iter) cout<<*iter<<" "; cout<<endl; hashtab.erase(12); for(iter = hashtab.begin();iter!= hashtab.end();++iter) cout<<*iter<<" "; cout<<endl; hashtab.erase(hashtab.find(13)); for(iter = hashtab.begin();iter!= hashtab.end();++iter) cout<<*iter<<" "; cout<<endl; } int main(){ test1(); }