#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
using namespace std;
//定义一个员工的类
class Employee{
public:
Employee(){name=""; salary=0.0; seniority=0;};// 初始构造函数
Employee(const string & n, double sal, int sen):name(n), salary(sal), seniority(sen){}//赋值构造函数
//获得该类的name成员
const string & getName() const//返回一个string类型的引用
{ return name; }
//重载==运算符 重载等号运算符 是为了下面查找键值的时候使用
bool operator==(const Employee & rhs) const
{ return getName() == rhs.getName(); }
//重载!=运算符
bool operator!=(const Employee & rhs) const
{ return !(*this == rhs); }
//设计一个友元函数 用于输出信息
friend ostream & operator<<(const ostream & os, const Employee & e){
cout << "name: " << e.name << ",\tsalary: " << e.salary << ",\tseniority: " << e.seniority;
}
private:
string name;//姓名
double salary;//薪水
int seniority;//级别
};
/****************************************************************
* 函数名称:hash(const HashedObj & key)
* 功能描述: 根据键值求个hash值,这个函数是根据一个特定的数学公式
* 参数列表: key 数据项的键值
* 返回结果:返回一个通过散列函数求得的值
*****************************************************************/
int hash(const string & key)
{
int hashVal = 0;
//用散列函数的那个公式求和
for(int i = 0; i < key.length(); ++i)
hashVal = 37*hashVal + key[i];
return hashVal;//这是通过散列函数求得的值
}
/****************************************************************
* 函数名称:hash(const HashedObj & key)
* 功能描述: 根据Employee引用的键值求个hash值,这个函数是根据一个特定的数学公式
* 参数列表: key 数据项的键值
* 返回结果:返回一个通过散列函数求得的值
*****************************************************************/
int hash(const Employee & item)
{
return hash(item.getName());
}
/****************************************************************
* 散列表类名称:HashTable
* 内容描述: 散列表类
*****************************************************************/
template<typename HashedObj>
class HashTable{
public:
explicit HashTable(int size = 11):array(nextPrime(size)), currentSize(0){makeEmpty();}//不能在隐式子转换中使用该函数
~HashTable();//析构函数
bool containes(const HashedObj & x);//判断是否包含数据项x
void makeEmpty();//清空散列表
bool isEmpty();//判断是否是空表
bool insert(const HashedObj & x);//插入项x
bool remove(const HashedObj & x);//删除项x
void print();//输出散列表中的内容
int findPos(const HashedObj & x);//根据名字查找数据项
HashedObj findElement(const HashedObj & x);//根据名字查找数据项,并返回
//表明位置的状态信息
enum EntryType {ACTIVE, EMPTY, DELETED};//每个数据单元都有一个info变量,表明该位置是否被占用、空或已删除
private:
//散列表的数据单元结构
struct HashEntry{
HashedObj element;//该散列表存放的数据
EntryType info;//表明该位置的状态 对应与82行
//结构的构造函数
HashEntry(const HashedObj & e = HashedObj(), EntryType i = EMPTY):element(e), info(i){}
};
vector<HashEntry> array;//散列表 这是一个array数组
int currentSize;//散列表中当前存放的元素个数
private:
void rehash();//再散列
int myhash(const HashedObj & x) const;//散列函数
int nextPrime(int n);//求的距离N最近的一个大于N的素数
int prePrime(int n);//求距离N最近的一个小于N的素数
bool isActive(int currentPos) const;//判断位置currentPos处的是否有元素
public:
//输出函数 输出这个点的信息
friend ostream & operator<<(const ostream & os, const HashEntry & e){
cout << "element: " << e.element << ", info = " << e.info;
}
};
/****************************************************************
* 函数名称:findElement(const HashedObj & x) const
* 功能描述: 查找x的位置
* 参数列表: x是要查找的元素
* 返回结果:如果找到则返回该元素的引用
*****************************************************************/
template<typename HashedObj>
HashedObj HashTable<HashedObj>::findElement(const HashedObj & x)
{
int currentPos = findPos(x);//找到x插入的位置
if(isActive(currentPos))//找了则返回 isActive()函数判断这个位置的状态 也就是判断这个位置是否有元素
return array[currentPos].element;//返回array数组这个位置的元素值
else{//没有找到,返回一个空值
HashedObj obj;
return obj;
}
}
/****************************************************************
* 函数名称:findPos(const HashedObj & x) const
* 功能描述: 查找x应该插入的位置
* 参数列表: x是要插入的元素
* 返回结果:如果找到空的位置则返回要插入的位置标号
*****************************************************************/
template<typename HashedObj>
int HashTable<HashedObj>::findPos(const HashedObj & x)
{
//线性探测f(i) = i; f(i) = f(i-1) + 1;相隔为1
//平方探测f(i) = i*i; f(i) = f(i-1) + 2*i - 1; 相隔为2*i-1
//双散列,f(i) = i*hash2(x); f(i) = f(i-1)+hash2(x);相隔为hash2(x);
//hash2(x) = R-(x%R); R=prePrime(array.size()),R为小于TableSize()的素数
int offset = 1;
int currentPos = myhash(x);
//如果找到了空的位置则返回位置标号
//如果找到了该元素x,则返回该元素的位置标号 找到这个位置前提的有元素 还要判断是否是要查找的元素
while(array[currentPos].info != EMPTY && array[currentPos].element != x){
//currentPos += offset;//线性探测
currentPos += 2 * offset -1;//平方探测
//currentPos += prePrime(array.size()) - myhash(x)%prePrime(array.size());//双散列
offset++;
if(currentPos >= array.size())
currentPos -= array.size();
}
return currentPos; //返回这个位置
}
/****************************************************************
* 函数名称:print()
* 功能描述: 输出散列表中的内容
* 参数列表: 无
* 返回结果:无
*****************************************************************/
template<typename HashedObj>
void HashTable<HashedObj>::print()
{
//<<已经被重载过了
cout << "输出散列表中的内容: " << endl;
for(unsigned i = 0; i < array.size(); ++i){
if(isActive(i))//如果这个位置有元素 就输出
cout << i << ": " << endl << array[i] << endl;
}
}
/****************************************************************
* 函数名称:isEmpty()
* 功能描述: 判断散列表是否为空
* 参数列表: 无
* 返回结果:无
*****************************************************************/
template<typename HashedObj>
bool HashTable<HashedObj>::isEmpty()
{
return currentSize == 0;//是判断当前散列表的个数 根据散列表中元素的个数来判断这个表是否是空表
}
/****************************************************************
* 函数名称:makeEmpty()
* 功能描述: 清空散列表
* 参数列表: 无
* 返回结果:无
*****************************************************************/
template<typename HashedObj>
void HashTable<HashedObj>::makeEmpty()
{
for(int i = 0; i < array.size(); ++i)
array[i].info = EMPTY;//把信息定义为空
currentSize = 0;//当前元素个数设为0
}
/****************************************************************
* 函数名称:containes(const HashedObj & x) const
* 功能描述: 判断散列表是否包含值为x的元素
* 参数列表: x数据项
* 返回结果:如果包括x则返回true,否则返回false
*****************************************************************/
template<typename HashedObj>
bool HashTable<HashedObj>::containes(const HashedObj & x)
{
//findPos(x)返回的位置是ACTIVE的说明存在该元素x
return isActive(findPos(x));//判断位置x是否有元素
}
/****************************************************************
* 函数名称:isActive(int currentPos) const
* 功能描述: 判断位置currentPos处的是否有元素
* 参数列表: currentPos是散列表currentPos处的位置
* 返回结果:如果currentPos处有元素则返回true,否则返回false
*****************************************************************/
template<typename HashedObj>
bool HashTable<HashedObj>::isActive(int currentPos) const
{
return array[currentPos].info == ACTIVE;//也就是查看这个点的状态 也就是比对状态信息是否是active
}
/****************************************************************
* 函数名称:remove(const HashedObj & x)
* 功能描述: 删除散列表中的值为x的元素
* 参数列表: x数据项
* 返回结果:成功删除返回true,否则返回false
*****************************************************************/
template<typename HashedObj>
bool HashTable<HashedObj>::remove(const HashedObj & x)
{
int currentPos = findPos(x);//查找x的位置 如果这点不存在 直接返回false
if(!isActive(currentPos))
return false;
array[currentPos].info = DELETED;//懒惰删除,仅仅将标识位info设置为Deleted
--currentSize;//元素个数减去1
return true;
}
/****************************************************************
* 函数名称:insert(const HashedObj & x)
* 功能描述: 在散列表中插入元素x,如果插入项已经存在,则什么都不做。
* 否则将其放在表的前端
* 参数列表: x数据项
* 返回结果:插入成功返回true, 否则返回false
*****************************************************************/
template<typename HashedObj>
bool HashTable<HashedObj>::insert(const HashedObj & x)
{
int currentPos = findPos(x);//先找到位置
if(isActive(currentPos))//如果该位置处已经存放了该元素,就是这里有与插入的值相等的元素,则之间返回false
return false;
array[currentPos] = HashEntry(x, ACTIVE);//把这个数据单元放到散列表中去
//如果当前散列表中元素的个数大于散列表长度的一半,则扩大散列表为原来的2倍
//散列表扩容
if(++currentSize > array.size()/2)
rehash();//扩充表的大小
return true;
}
/****************************************************************
* 函数名称:~HashTable()
* 功能描述: 析构函数
* 参数列表: 无
* 返回结果:无
*****************************************************************/
template<typename HashedObj>
HashTable<HashedObj>::~HashTable()
{
}
/****************************************************************
* 函数名称:prePrime(int n)
* 功能描述: 获得距离n最近的一个小于n的素数
* 参数列表: n表示数值
* 返回结果:返回一个素数
*****************************************************************/
template<typename HashedObj>
int HashTable<HashedObj>::prePrime(int n)
{
int i;
if(n % 2 == 0)
n--;
for(; ; n -= 2){
for(i = 3; i*i <= n; i += 2){//i是从3开始 对其取余数 然后判断是否是素数 如果是则返回
if(n % i == 0)
goto ContOuter;//这个的作用就是跳出这个循环
}
return n;
ContOuter: ;
}
}
/****************************************************************
* 函数名称:nextPrime(int n)
* 功能描述: 获得距离n最近的一个大于n的素数
* 参数列表: n表示数值
* 返回结果:返回一个素数
*****************************************************************/
template<typename HashedObj>
int HashTable<HashedObj>::nextPrime(int n)
{
int i;
if(n % 2 == 0)//要求得是素数 首先排除偶数这一选项
n++;
for(; ; n += 2){
for(i = 3; i*i <= n; i += 2)
if(n % i == 0)
goto ContOuter;
return n;
ContOuter: ;
}
}
/****************************************************************
* 函数名称:rehash()
* 功能描述: 扩大散列表的大小
* 参数列表: 无
* 返回结果:无
*****************************************************************/
template<typename HashedObj>
void HashTable<HashedObj>::rehash()
{
vector<HashEntry> oldArray = array;
//创建一个新的大小为原来两倍大小的散列表
array.resize(nextPrime(2 * oldArray.size()));
//先将其初始化为空
for(int i = 0; i < array.size(); ++i)
array[i].info = EMPTY;
currentSize = 0;
//复制散列表 将原来散列表的信息复制过来
for(int i = 0; i < oldArray.size(); ++i){
if(oldArray[i].info == ACTIVE)
insert(oldArray[i].element);
}
}
/****************************************************************
* 函数名称:myhash(const HashedObj & key)
* 功能描述: 根据键值求个hash值
* 参数列表: key 数据项的键值
* 返回结果:返回hash值
*****************************************************************/
template<typename HashedObj>
int HashTable<HashedObj>::myhash(const HashedObj & key) const
{
int hashVal = hash(key);
hashVal %= array.size();//根据键值获得哈希值 取余数的的除数设置为这个数组的长度
if(hashVal < 0)
hashVal += array.size();
return hashVal;
}
int main()
{//先初始化这些数据
Employee e1("linux", 101.00, 1);
Employee e2("ever", 102.00, 2);
Employee e3("peter", 103.00, 3);
Employee e4("may", 104.00, 4);
Employee e5("usa", 105.00, 5);
Employee e6("sal", 106.00, 6);
Employee e7("usa", 107.00, 7);//和上面的值重复,所以这个值会被忽略
Employee e8("jan", 108.00, 8);
Employee e9("kro", 109.00, 9);
Employee e10("bei", 110.00, 10);
Employee e12("bbb", 110.00, 10);
//再讲这些值放入到数组中去
vector<Employee> v;
v.push_back(e1);
v.push_back(e2);
v.push_back(e3);
v.push_back(e4);
v.push_back(e5);
v.push_back(e6);
v.push_back(e7);
v.push_back(e8);
v.push_back(e9);
v.push_back(e10);
cout << "v: " << endl;
//先输出这个哈希散列表的值
for(unsigned i = 0; i < v.size(); ++i)
cout << v[i] << endl;
cout << endl;
HashTable<Employee> hashTable;
for(unsigned i = 0; i < v.size(); ++i)
//那个散列表数组中的元素都插入到哈希表中去
hashTable.insert(v[i]);
//输出哈希表的信息
hashTable.print();
cout << endl;
cout << "测试包含函数containes: " << endl;
if(hashTable.containes(e10))
cout << "containe e10" << endl;
else
cout << "not containe e10" << endl;
if(hashTable.containes(e12))
cout << "containe e12" << endl;
else
cout << "not containe e12" << endl;
cout << "\n测试findElement(): " << endl;
Employee e11 = hashTable.findElement(e8);
cout << "e11: " << e11 << endl;
cout << endl;
cout << "测试isEmpty(): " << endl;
if(hashTable.isEmpty())
cout << "hashTable is Empty " << endl;
else
cout << "hashTable is not Empty " << endl;
cout << endl;
cout << "测试makeEmpty(): " << endl;
hashTable.makeEmpty();
if(hashTable.isEmpty())
cout << "hashTable is Empty " << endl << endl;
else
cout << "hashTable is not Empty " << endl;
return 0;
}
下面是运行的结果
v:
name: linux, salary: 101, seniority: 1
name: ever, salary: 102, seniority: 2
name: peter, salary: 103, seniority: 3
name: may, salary: 104, seniority: 4
name: usa, salary: 105, seniority: 5
name: sal, salary: 106, seniority: 6
name: usa, salary: 107, seniority: 7
name: jan, salary: 108, seniority: 8
name: kro, salary: 109, seniority: 9
name: bei, salary: 110, seniority: 10
输出散列表中的内容:
1:
element: name: kro, salary: 109, seniority: 9, info = 0
3:
element: name: jan, salary: 108, seniority: 8, info = 0
4:
element: name: may, salary: 104, seniority: 4, info = 0
5:
element: name: bei, salary: 110, seniority: 10, info = 0
6:
element: name: usa, salary: 105, seniority: 5, info = 0
17:
element: name: ever, salary: 102, seniority: 2, info = 0
18:
element: name: sal, salary: 106, seniority: 6, info = 0
21:
element: name: peter, salary: 103, seniority: 3, info = 0
22:
element: name: linux, salary: 101, seniority: 1, info = 0
测试包含函数containes:
containe e10
not containe e12
测试findElement():
e11: name: jan, salary: 108, seniority: 8
测试isEmpty():
hashTable is not Empty
测试makeEmpty():
hashTable is Empty