【基本要求】
(1)设每个记录有下列数据项:电话号码、用户名、地址;
(2)从键盘输入各记录,分别以电话号码和用户名为关键字建立散列表;
(3)采用一定的方法解决冲突;
(4)查找并显示给定电话号码的记录;
(5)查找并显示给定用户名的记录。
【选做内容】
(1)系统功能的完善;
(2)设计不同的散列函数,比较冲突率;
(3)在散列函数确定的前提下,尝试各种不同类型处理冲突的方法,考察平均查找长度的变化。
用的C++开发,基本实现了3种哈希函数+3种解决冲突的方法。因为要求同时有姓名散列与按号码散列,所以用了
flag标记每次的散列类型 ,针对不同的要求对散列函数做了个别优化。
哈希表类的结构如下


1 class HashTable{ 2 public: 3 HashTable(int size = MAXSIZE-1); 4 ~HashTable(){ delete[]E; delete[]tag; delete[]E2; delete[]tag2; } 5 int hash1(string name, int flag);//哈希函数1 除数求余法 6 int hash2(string tel);//哈希函数2 折叠法 7 int hash3(string tel);//哈希函数3 数字分析法 8 int solve1(int hashVal, int flag);//线性探测法解决冲突 9 int solve2(int hashVal, int flag);//二次探测法解决冲突 10 Node* solve3(int hashVal, int flag);//拉链法解决冲突 11 User input();//往电话薄中添加用户 12 void creat(int flag); //创建散列表 13 void show(int flag); //列出电话薄所有元素 14 void search(int flag,string at); //搜索指定用户 15 void searchByNode(int flag, string at); //拉链法搜索指定用户 16 void insert(int flag, User newUser); //插入 17 void del(int flag, string by);//删除 18 void save(int flag);//将电话薄保存至本地文件 19 int length; //要创建的电话本长度 20 Node** ht; 21 private: 22 User* E; //用户数组 按姓名散列 23 User* E2; //用户数组2 按电话号码散列 24 int* tag; //标记散列表1每个桶的存储状态 0为空 1为实 25 int* tag2;//标记散列表2每个桶的存储状态 26 int flag; //1表示是按姓名 2表示按电话号码 新建的哈希表 27 int maxSize; //哈希表最大长度 28 int f;//比例因子 主要用于折叠法 29 };
User类的结构
class User{ public: string name; string tel; string address; bool operator==(const User&target) { if (this->name == target.name&&this->address == target.address&&this->tel == target.tel) return true; else return false; } };
哈希函数1
int HashTable::hash1(string name,int flag) //除留求余法 { int hashValue; long a = 0; switch (flag) { case 1: for (int i = 0; i < name.length(); i++) a += int(name[i]); hashValue = a%maxSize; break; case 2: int temp = atof(name.c_str()); hashValue = temp%maxSize; break; } return hashValue; };
哈希函数2
int HashTable::hash2(string tel) //折叠法--移位法 { int hashValue; int temp; //移位法求和 temp = atof(tel.substr(0, 3).c_str()) + atof(tel.substr(3, 3).c_str()) + atof(tel.substr(6, 3).c_str()) + atof(tel.substr(9, 2).c_str()); //取计算之后的数的最后三位 if (temp >= 999) { char p[10]; sprintf(p, "%d", temp); string lastThree = p; lastThree = lastThree.substr(lastThree.length() - 3, 3); hashValue = atof(lastThree.c_str()); return hashValue; } hashValue = temp; return hashValue; };
哈希函数3
int HashTable::hash3(string tel)//数字分析法做哈希函数 { int hashValue; hashValue = atof(tel.substr(8, 3).c_str()); //因为电话号码一般后4位不同 return hashValue; };
解决冲突的方法
1.线性探测法
int HashTable::solve1(int hashVal,int flag) //线性探查法处理冲突 { int output = hashVal; switch (flag) { case 1: for (int j = 1; j < MAXSIZE; j++) { output = (hashVal + j) % MAXSIZE; if (tag[output] == 0) { tag[output] = 1; return output; } } return -1; break; case 2: for (int j = 1; j < MAXSIZE; j++) { output = (hashVal + j) % MAXSIZE; if (tag2[output] == 0) { tag2[output] = 1; return output; } } return -1; default: break; } };
2.二次探查法
int HashTable::solve2(int hashVal, int flag) //二次探查法解决冲突 { int i = hashVal; //i为初始桶号 int k = 0; //k为探查次数 int odd = 0; //odd为控制加减的标志 int save; //缓存上一次的桶号 switch (flag) { case 1: while (tag[i]==1) { if (odd == 0) { k++; save = i; i = (i + 2 * k-1) % MAXSIZE; odd = 1; } else { i = (save - 2 * k+1) % MAXSIZE; odd = 0; if (i<0) { i = i + MAXSIZE; } } } return i; break; case 2: while (tag2[i] == 1) { if (odd == 0) { k++; save = i; i = (i + 2 * k - 1) % MAXSIZE; odd = 1; } else { k++; i = (save - 2 * k + 1) % MAXSIZE; odd = 0; if (i<0) { i = i + MAXSIZE; } } } return i; break; default: break; } };
3.拉链法
<