//---------------------------15/03/24----------------------------

//hashtable

{

/*

概述:

sgi采用的是开链法完成hashtable的,也就是用链表来存储映射到相同位置的元素。

*/

//node(节点)

template<class Value>

struct __hashtable_node

{

__hashtable_node* next;

Value val;

};

//bucket维护的linked list不是stl的list或slist,而是自行维护的,table则是用vector完成以便扩充

//迭代器

template<class Value,
class Key, class HashFcn,
class ExtractKey,

class EqualKey, class Alloc>

struct __hashtable_iterator

{

typedef hashtable<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>

hashtable;

typedef __hashtable_iterator<Value, Key, HashFcn, ExtractKey,

EqualKey, Alloc>

iterator;

typedef __hashtable_const_iterator<Value, Key, HashFcn, ExtractKey,

EqualKey, Alloc>

const_iterator;

typedef __hashtable_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;

__hashtable_iterator(node* n, hashtable* tab): cur(n), ht(tab){}

__hashtable_iterator(){}

reference
operator*() const {return cur->val;}

pointer
operator->() const {return &(operator*());}

iterator&
operator++();

iterator
operator++(int);

bool operator==(const iterator& it)
const {return cur == it.cur;}

bool operator!=(const iterator& it)
const {return cur != it.cur;}

};

//operator++()

template<class V,
class K, class HF,
class ExK, class EqK,
class A>

__hashtable_iterator<V, K, HF, ExK, EqK, A>&

__hashtable_iterator<V, K, HF, ExK, EqK, A>::operator++()

{

const node* old = cur;

//cur是当前节点,用->next取得下个节点,如果这个位置已经没有更多节点,cur就是null

cur = cur->next;

if(!cur)

{

//如果没有更多节点,使用val取得之前的cur在table中的位置

size_type bucket = ht->bkt_num(old->val);

//只要cur为null
并且 下一个位置小于最大值
就让cur为下一个位置的节点(如果存在就有值,不然为null)

while(!cur && ++bucket < ht->buckets.size())

cur = ht->buckets[bucket];

}

//如果++没有找到下一个元素(之后没有元素了),cur就为ht->buckets[buckets.size()-1]==null

//返回的iterator指向null,否则指向下一个节点

return *this;

}

template<class V,
class K, class HF,
class ExK, class EqK,
class A>

__hashtable_iterator<V, K, HF, ExK, EqK, A>&

__hashtable_iterator<V, K, HF, ExK, EqK, A>::operator++(int)

{

iterator tmp = *this;

++*this;

return tmp;

}

//没有--操作

//class声明式

template<class Value,
class Key, class HashFcn,
class ExtractKey,

class EqualKey, class Alloc=alloc>

class hashtable;

//class定义

//Value:节点的实值类型   Key:节点的键值类型   HashFcn:hash function的函数类型

//ExtractKey:
从节点中取出键值的方法(可以使函数或仿函数) EqualKey:判断键值相同与否的方法(函数或仿函数)

template<class Value,
class Key, class HashFcn,
class ExtractKey,

class EqualKey, class Alloc>

class hashtable

{

public:

typedef HashFcn hasher;

typedef EqualKey key_equel;

typedef size_t size_type;

private:

hasher  hash;

key_equel equals;

ExtractKey get_key;

typedef __hashtable_node<Value> node;

typedef simple_alloc<node, Alloc> node_allocator;

vector<node*, Alloc> buckets;

size_type num_elements;

public:

size_type bucket_count()
const {return buckets.size();}

};

//虽然开链法不要求表格大小必须为质数,但是sgi stl还是用质数大小来设计表格(这样碰撞的机会会变小)

//而且先把28个质数计算好,以备随时访问,同时提供一个函数来查询最适合的质数

static const
;

static const
unsigned long __stl_prime_list[__stl_num_primes]=

{

53,
97, 193...
4294967291ul

};

//获取大于n的下一个质数,不会超过last-1

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;

//使用全局算法lower_bound获取n的位置,把指针当迭代器来用

const unsigned
long* pos = lower_bound(first, last, n);

) : *pos;

}

size_type max_bucket_count()
const

{

];

}

//构造与内存管理

template<class Value,
class Key, class HashFcn,
class ExtractKey,

class EqualKey, class Alloc>

class hashtable

{

typedef simple_alloc<node, Alloc> node_allocator;

//创建和销毁节点,和以前类型

node* new_node(const value_type& obj)

{

node* n =node_allocator::allocate();

n->next =
;

__STL_TRY

{

construct(&n->val, obj);

return n;

}

__STL_UNWIND(node_allocator::deallocate(n));

}

void delete_node(node* n)

{

destroy(&n->val);

node_allocator::deallocate(n);

}

//构造函数,设置好hash函数,判断相等的函数,取得键值(键值是用来比较是否相等的)的函数

//根据n设置好table的大小

hashtable(size_type n,
const HashFcn& hf,
const EqualKey& eql)

:hash(hf), equals(eql), get_key(ExtractKey()), num_elements()

{

initialize_buckets(n);

}

//initialize_buckets

void initialize_buckets(size_type n)

{

//找到合适的质数

const size_type n_buckets = next_size(n);

//保留 n_buckets大小的空间

buckets.reserve(n_buckets);

buckets.insert(buckets.end(), n_buckets, (node*));

num_elements =
;

}

//insert

pair<iterator,
bool> insert_unique(const value_type& obj)

{

resize(num_elements +
);

return insert_unique_noresize(obj);

}

iterator insert_equal(const value_type& obj)

{

resize(num_elements +
);

return insert_equal_noresize(obj);

}

//取得位置,首先要根据get_key函数
取得键值,只要针对非数值类型的变量

//比如字符串,string或者自定义类对象(对于这些我们要自己写好get_key函数传入模版参数)

//n表示取模的大小,最后要先调用hash函数,然后取模(默认就是table的大小)

size_type bkt_num(const value_type& obj, size_t n)
const

{

return bkt_num_key(get_key(obj), n);

}

//使用默认的取模大小

size_type bkt_num(const value_type& obj)
const

{

return bkt_num_key(get_key(obj));

}

//使用默认的取模大小

size_type bkt_num_key(const key_type& key)
const

{

return bkt_num_key(key, buckets.size());

}

//真正的取位置操作,根据传入的键值,先调用hash函数,然后取模

size_type bkt_num_key(const value_type& obj, size_t n)
const

{

return hash(key) % n;

}

//find

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)

{}

//不存在this就是null,会返回false,否则就是true

return iterator(first,
this);

}

//count

size_type count(const key_type& key)
const

{

//找到键值位置

const size_type n =bkt_num_key(key);

size_type result =
;

//找到链表中和键值相等的元素个数

for(const node* cur = buckets[n]; cur; cur = cur->next)

if(equals(get_key(cur->val), key))

++result;

return result;

}

};

//resize

template<class V,
class K, class HF,
class Ex, class Eq,
class A>

__hashtable_iterator<V, K, HF, Ex, Eq, A>&

__hashtable_iterator<V, K, HF, Ex, Eq, A>::resize(size_type num_elements_hint)

{

//纪录原来table的大小

const size_type old_n = buckets.size();

if(num_elements_hint > old_n)

{

//如果新大小大于原来table大小

//找到下一个合适的质数

const size_type n = next_size(num_elements_hint);

if(n > old_n)

{

//这里如果之前n就是最后一个质数了,就不会进入这个块语句了

//初始化n大小的为0的vector

vector<node*, A> tmp(n, (node*));

; bucket < old_n; ++bucket)

{

//从原来的table中一个个复制到新table中

node* first = buckets[bucket];

while(first)

{

size_type new_bucket = bkt_num(first->val, n);

buckets[bucket] = first->next;

first->next = tmp[new_bucket];

tmp[new_bucket] = first;

first = buckets[bucket];

}

}

//交换新的table为当前table

buckets.swap(tmp);

}

}

}

//insert_unique_noresize

template<class V,
class K, class HF,
class Ex, class Eq,
class A>

__hashtable_iterator<V, K, HF, Ex, Eq, A>&

__hashtable_iterator<V, K, HF, Ex, Eq, A>::insert_unique_noresize(const value_type& obj)

{

//插入操作不可重复

//找到位置

const size_type n =bkt_num(obj);

//第一个节点

node* first = buckets[n];

//查找节点,如果有重复的键值,就返回false

for(node* cur = first; cir; 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);

}

//insert_equal_noresize

template<class V,
class K, class HF,
class Ex, class Eq,
class A>

__hashtable_iterator<V, K, HF, Ex, Eq, A>&

__hashtable_iterator<V, K, HF, Ex, Eq, A>::insert_equal_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)))

{

node* tmp =new_node(obj);

tmp->next = cur->next;

cur->next = tmp;

++num_elements;

return iterator(tmp,
this);

}

node* tmp = new_node(obj);

tmp->next = first;

buckets[n] = tmp;

++num_elements;

return iterator(tmp,
this);

}

//clear

template<class V,
class K, class HF,
class Ex, class Eq,
class A>

__hashtable_iterator<V, K, HF, Ex, Eq, A>&

__hashtable_iterator<V, K, HF, Ex, Eq, A>::clear()

{

//从头部一个个删除节点

; i < buckets.size(); ++i)

{

node* cur = buckets[i];

)

{

node* next = cur->next;

delete_node(cur);

cur = next;

}

buckets[i] =
;

}

num_elements =
;

}

//copy_from

template<class V,
class K, class HF,
class Ex, class Eq,
class A>

__hashtable_iterator<V, K, HF, Ex, Eq, A>&

__hashtable_iterator<V, K, HF, Ex, Eq, A>::copy_from(const hashtable& ht)

{

//调用buckets.clear()
先清除原来的table,再插入要copy的各个节点

//疑惑:
为什么不先调用一次clear(),不这样
原来table中的节点不是都没释放么?

//不是会造成内存泄漏?

buckets.clear();

buckets.reserve(ht.buckets.size());

bucket.insert(buckets.end(), ht.buckets.size(), (node*)
);

__STL_TRY

{

; i < ht.buckets.size(); ++i)

{

if(const node* cur = ht.buckets[i])

{

node* copy = new_node(cur->val);

bucket[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;

}

__STL_UNWIND(clear());

}

template<class Key>

struct hash{};

//计算key值函数,计算char*类型的 key值:
h(n)=h(n-1)*5 + *s   *s就是(*s)的ascii码值

//
这样计算就使得字符与其位置有关,如果少了h(n-1)*5 那么 asd
和dsa 就会计算得到相同的位置

inline size_t __stl_hash_string(const
char* s)

{

unsigned long h =
;

for( ; *s; ++s)

h =
*h + *s;

return size_t(h);

}

//模版特化

//仿函数:利用 __stl_hash_string
取得key值

__STL_TEMPLATE_NULL
struct hash<char*>

{

size_t
operator()(const
char* s) const {return __stl_hash_string(s);}

};

/*

总结:hashtable就是一个大小为质数的vector中存放一条链表,查找和存放都要利用hash函数取得

对应的位置,然后存放到链表中,hash函数可以自定义(自己针对自己的类型进行特化),hashtable的hash

函数正确来说其实就是取模操作(默认操作),上面说的hash函数只是给出一个键值(当然也可以在这里自定义hash函数,

比如加个质数什么的),最后当然还是要取模运算。

最后就是copy_from里为什么没有调用clear(),这样应该会造成内存泄漏,当然很可能是我想错了,毕竟这是大牛

们写的
我也查过最新的sgi stl 上面还是这么写的。

*/

}

04-16 20:36