散列的基本思想:
理想的散列表数据结构只不过是一些包含一些项的具有固定大小的数组。项一般由键和数据成员构成,表的大小为散列数据的一部分,通常表的大小从0到,这为散列函数带来了处理上面的方便。
散列函数:
将每个键映射到0到范围内的某个数,并将其放到适当的存储单元中。理想情况下散列函数应该计算简单而且保证任何两个不同的键映射到不同的单元,实际上这是不可能实现的,因为键值无限而存储单元有界,因此需要寻找一个散列函数能够“尽可能均匀”的分布键,同时,散列函数还具体解决当两个键映射到同一单元时(称为冲突)问题及调整表的大小的功能。
一个较好的散列函数:,即计算37的多项式函数。该散列函数允许溢出,可能产生负数,通过修正该溢出,使用取模运算保证键值仍在存储单元范围内。
冲突(collision):
当两个键映射到同一单元时将发生存储冲突。解决冲突的两种常用方法:分离链接法(sparate chaining)和开放定址法。下图为分离链接法图示(此处,通常模数为素数,将更有利于数据的删除和插入操作),即将同一值得所有元素保存到同一链表中。
装填因子(load factor) : .
数学推论证明,对于分离链接法,散列表的大小并不重要,而装填因子才是最重要的,对于分离散列表而言,应满足:,当装填因子超过1时,使用再散列方法重新分配存储单元的大小。
再散列:
重新分配存储单元大小,通常为大于原始表 2倍的最小素数。
散列优点:
以常数平均时间执行插入、删除、查找的技术。
实例:创建基于分离链接法的散列表,完成插入、删除、再散列、查询、清除等功能。
1、hash.h
//hash.h
#ifndef HASH_H_
#define HASH_H_
#include<iostream>
#include<string>
#include<vector>
#include<list>
#include<algorithm>
#include<cmath>
#include<iomanip>
using std::cout;
using std::endl;
using std::string;
using std::ostream;
using std::vector;
using std::list;
using std::setw;
class Student{
private:
string name;
string course;
double score;
public:
//Student constructor
Student(const string &n, const string &c, double s):name(n), course(c), score(s){
};
//non parameter constructor
Student():name(""), course(""), score(0.0){
}
//Student destructor
~Student(){
};
//get name
const string & getName() const{
return name;
};
//overload operator==
bool operator==(const Student &stu) const{
return name == stu.name;
};
//overload operator!=
bool operator!=(const Student &stu) const{
return !(*this == stu);
};
//overload operator<<
friend ostream & operator<<(ostream &os, const Student &stu){
cout << " name : " << stu.name << setw(8) << " course : " << setw(8) << stu.course << " score : " << stu.score << endl;
return os;
}
};
//hash function
int hash(const string &key){
int hashVal = 0;
for(int i = 0; i < key.length(); i++){
hashVal = 37 * hashVal + key[i];
}
return hashVal;
};
//overload hash function
int hash(const Student &stu){
return hash(stu.getName());
};
template<class T>
class HashTable{
private:
vector<list<T> > table;
int currentSize;
public:
//HashTable constructor
HashTable(int num = 11):table(num), currentSize(0){
};
//HashTable destructor
~HashTable(){
};
//judge whether the table contain obj return T obj
T findElement(const T &obj){
list<T> &temp = table[myhash(obj)];
typename list<T>::iterator itr = find(temp.begin(), temp.end(), obj);
if(itr != temp.end()){
return *itr;
}else{
T temp1;
return temp1;
}
};
//judge whether table contain obj reutrn bool
bool contain(const T &obj) const{
const list<T> &temp = table[myhash(obj)];
return find(temp.begin(), temp.end(), obj) != temp.end();
};
//empty the table
void makeEmpty(){
for(int i = 0; i < table.size(); i++){
table[i].clear();
}
currentSize = 0;
};
//judge whether table is empty
bool isEmpty(){
return currentSize == 0;
};
//insert obj into table
bool insert(const T &obj){
list<T> &temp = table[myhash(obj)];
//judge whether obj exist in table
if(find(temp.begin(), temp.end(), obj) != temp.end()){
return false;
}
temp.push_back(obj);
if(++currentSize > table.size()){
rehash();
}
return true;
};
//remove obj outof table
bool remove(const T &obj){
list<T> &temp = table[myhash(obj)];
typename list<T>::iterator itr = find(temp.begin(), temp.end(), obj);
if(itr == temp.end()){
return false;
}
temp.erase(itr);
currentSize--;
return true;
};
//print the table
void print(){
cout << " the table info as follows : " << endl;
for(int i = 0; i < table.size(); i++){
cout << i << " : " << endl;
for(typename list<T>::iterator itr = table[i].begin(); itr != table[i].end(); ++itr){
cout << *itr;
}
}
};
private:
//rehash operation
void rehash(){
vector<list<T> > temp = table;
table.resize(nextPrime(table.size()));
for(int i = 0; i < table.size(); i++){
table[i].clear();
}
for(int i = 0; i < temp.size(); i++){
typename list<T>::iterator itr = temp[i].begin();
while(itr != temp[i].end()){
insert(*itr);
}
}
};
//hash function
int myhash(const T &obj) const{
int hashVal = hash(obj);
hashVal %= table.size();
if(hashVal < 0){
hashVal += table.size();
}
return hashVal;
};
//find next prime while is bigger than the twice of currentSize
int nextPrime(int n){
//set flag
bool flag = true;
for(int i = 2 * n; ; i++) {
for(int j = 2;j < i; j++) {
if(i % j == 0) {
//not a prime
flag = false;
break;
}
}
//find the prime
if(flag) {
return i;
break;
}
flag = true;
}
};
};
#endif
2、main.cpp
#include<iostream>
#include"hash.h"
using namespace std;
int main(){
Student stu[6] = {
Student("Aimi", "python", 80.6),
Student("Brone", "C++", 90.3),
Student("Cver", "python", 85.6),
Student("David", "C++", 89.7),
Student("Eular", "java", 85.0),
Student("Frank", "C++", 92.1)
};
vector<Student> vstu;
for(int i = 0; i < 6; i++){
vstu.push_back(stu[i]);
}
for(int i = 0; i < 6; i++){
cout << vstu[i];
}
HashTable<Student> hashTable;
for(int i = 0; i < 6; i++){
hashTable.insert(vstu[i]);
}
cout << "******** print ********" << endl;
hashTable.print();
cout << "******** contain ********" << endl;
if(hashTable.contain(stu[0])){
cout << " contain " << stu[0] << endl;
}else{
cout << "does not exist the object !" << endl;
}
cout << "******** findElement ********" << endl;
cout << hashTable.findElement(stu[1]) << endl;
cout << "******** insert ********" << endl;
hashTable.insert(Student("Furier", "java", 89.0));
hashTable.print();
cout << "******** remove ********" << endl;
hashTable.remove(stu[3]);
hashTable.print();
return 0;
}
practice makes perfect !