散列表(哈希表)介绍
2013-12-27 09:48
351 查看
散列表的定义:
散列表是一种数据结构。理想的散列表数据结构是一个具有固定大小的数组。
散列函数:
对于数组的每一个对象,都会有一个关键字,我们成为键值,例如数据对象是一个字符串,就可以直接作为键值,如果数据对象是一个类,那可以取其中的某一个成员变量作为键值。将每个键值映射到从0到tableSize-1这个范围中的某个数,并且将其放到适当的单元中,这个映射就称为散列函数。理想情况下,它应该运算简单并且应该保证任何两个不同的键映射到不同的单元。
冲突:
理想的散列函数是不可能存在的,因为单元的数目是有限的,而键值实际上是用不完的。当两个键值散列到同一个单元的时候,这种情况称为冲突。解决冲突,也是散列表的重要的特征。
解决冲突的方法之分离链表法:
分离链表法,其做法是将散列到同一个值的所有元素保留到一个链表中。举个代码例子:
解决冲突方法之探测散列:
散列表是一种数据结构。理想的散列表数据结构是一个具有固定大小的数组。
散列函数:
对于数组的每一个对象,都会有一个关键字,我们成为键值,例如数据对象是一个字符串,就可以直接作为键值,如果数据对象是一个类,那可以取其中的某一个成员变量作为键值。将每个键值映射到从0到tableSize-1这个范围中的某个数,并且将其放到适当的单元中,这个映射就称为散列函数。理想情况下,它应该运算简单并且应该保证任何两个不同的键映射到不同的单元。
冲突:
理想的散列函数是不可能存在的,因为单元的数目是有限的,而键值实际上是用不完的。当两个键值散列到同一个单元的时候,这种情况称为冲突。解决冲突,也是散列表的重要的特征。
解决冲突的方法之分离链表法:
分离链表法,其做法是将散列到同一个值的所有元素保留到一个链表中。举个代码例子:
///***************************HashMap****************************/ //1.用数组形式建立哈希表 //2.使用链式方法解决冲突 //3.平方取中法通过字符串Hash函数求hash值(还有很多种其他的hash函数) #include <iostream> #include <string> #include <cstring> #include <stdio.h> #include <stdlib.h> #include <time.h> using namespace std; typedef unsigned long(*GetKeyValue)(const string& ); //该类用来处理冲突的节点 template<class TypeA,class TypeB> struct HashNode{ TypeA key; TypeB value; HashNode *next; HashNode(TypeA key,TypeB value){ HashNode::key = key; HashNode::value = value; next = NULL; } HashNode& operator=(const HashNode& node){ key = node.key; value = node.key; next = node.next; return *this; } }; //该类是HashMap用来存放hash表 template<class TypeA,class TypeB,class FuncType> class HashMap { HashNode<TypeA,TypeB> **table; unsigned long capacity; FuncType GetKeyValue; const TypeB TYPEB_NULL; public: HashMap(FuncType func,const TypeB& _null); ~HashMap(); TypeB Put(const HashNode<TypeA,TypeB>& hashNode); //插入一个HashNode 返回该节点的value值 TypeB GetValue(const TypeA& key); // 查找某个hashNode 其key为“key“的元素 TypeB Delete(const TypeA& key); // 查找某个hashNode 其key为“key“的元素 }; template<class TypeA,class TypeB,class FuncType> //在调用的时候指定函数 HashMap<TypeA,TypeB,FuncType>::HashMap(FuncType func,const TypeB& _null) : TYPEB_NULL(_null) { capacity = 10000000; GetKeyValue = func; table = new HashNode<TypeA,TypeB>*[capacity]; for(unsigned i = 0;i < capacity;i++) table[i] = NULL; } template<class TypeA,class TypeB,class FuncType> HashMap<TypeA,TypeB,FuncType>::~HashMap(){ for(unsigned i = 0;i < capacity;i++){ HashNode<TypeA,TypeB> *currentNode = table[i]; while(currentNode) { HashNode<TypeA,TypeB> *temp = currentNode; currentNode = currentNode->next; delete temp; } } delete table; } //新增节点操作,用鏈式法解決衝突 template<class TypeA,class TypeB,class FuncType> TypeB HashMap<TypeA,TypeB,FuncType>::Put(const HashNode<TypeA,TypeB>& hashNode){ HashNode<TypeA,TypeB> *newNode = NULL; unsigned long index = GetKeyValue(hashNode.key); if(table[index] == NULL){ table[index] = new HashNode<TypeA,TypeB>(hashNode.key,hashNode.value); newNode = table[index]; } else{ newNode = table[index]; while(newNode->next){ newNode = newNode->next; } newNode->next = new HashNode<TypeA,TypeB>(hashNode.key,hashNode.value); newNode = newNode->next; } return newNode->value; } //由键值获得value template<class TypeA,class TypeB,class FuncType> TypeB HashMap<TypeA,TypeB,FuncType>::GetValue(const TypeA& key){ unsigned long index = GetKeyValue(key); if(table[index] == NULL) return TYPEB_NULL; else{ HashNode<TypeA,TypeB> *currentNode = table[index]; while(currentNode){ if(currentNode->key == key) return currentNode->value; currentNode = currentNode->next; } } return TYPEB_NULL; } //由键值查找后,删除该节点,返回该删除的节点的value template<class TypeA,class TypeB,class FuncType> TypeB HashMap<TypeA,TypeB,FuncType>::Delete(const TypeA& key){ TypeB deletedNodeValue = TYPEB_NULL; unsigned long index = GetKeyValue(key); if(table[index] == NULL) return deletedNodeValue; else{ HashNode<TypeA,TypeB> *currentNode = table[index]; if(currentNode->key == key){ table[index] = currentNode->next; deletedNodeValue = currentNode->value; delete currentNode; return deletedNodeValue; } while(currentNode){ if(currentNode->next->key == key){ HashNode<TypeA,TypeB> *temp = currentNode->next; currentNode->next = currentNode->next->next; deletedNodeValue = temp->value; delete temp; return deletedNodeValue;; } currentNode = currentNode->next; } } return deletedNodeValue; } /***************************************测试****************************/ //平方取中法 //实现,取字符串中间3个字母,不足3个用0补足 unsigned long GetKeyValue_1(const string& key){ unsigned long keyValue = 0; int strSize = key.size(); string tempStr(key); for(int i = strSize;i < 3;i++) tempStr = "0" + tempStr; //如果大于3就取中间3位 if(strSize >= 3){ tempStr[0] = key[strSize / 2 - 1]; tempStr[1] = key[strSize / 2]; tempStr[2] = key[strSize / 2 + 1]; } tempStr = tempStr.substr(0,3); unsigned long num = 10000 * (unsigned long)(48); num += 100 * (unsigned long)(tempStr[1]); num += (unsigned long)(tempStr[2]); num *= num; char *numStr = new char[15]; snprintf(numStr,10,"%d",num) ; int strLen = strlen(numStr); tempStr = "000000"; for(int i = -2;i < 4;i++) tempStr[2 + i] = numStr[strLen / 2 + i]; keyValue = strtol(tempStr.c_str(),NULL,10); delete []numStr; return keyValue; } int main() { clock_t start = clock(); //传入一个求哈希散列值的方法GetKeyValue_1 HashMap<string,string,GetKeyValue> hashMap(GetKeyValue_1,"NULL"); for(int i = 0;i < 100000;i++){ char *ckey = new char[20]; char *cvalue = new char[20]; snprintf(ckey,10,"%d",i); snprintf(cvalue,10,"%d",i); string key(ckey); string value(cvalue); if(i == 67){ key = "67"; value = "hello hash No.67"; } HashNode<string,string> node1(key,value); //插入该节点 hashMap.Put(node1); // cout << "index: " <<GetKeyValue_1(key) << " nodeValue: " // << hashMap.Put(node1) << endl; } clock_t end = clock(); cout << "Test Result: \n"; //调用了time.h文件的CLOCKS_PER_SEC,表示每过千分之一秒,clock()函数返回值加1 cout << "插入100000条数据耗时: " << double(end - start) / CLOCKS_PER_SEC << " 秒" << endl; cout << "hashMap.GetValue(\"67\"): " << hashMap.GetValue("67") << endl; cout << "hashMap.Delete(\"67\"): " << hashMap.Delete("67") << endl; cout << "hashMap.GetValue(\"67\"): " << hashMap.GetValue("67") << endl; return 0; }
解决冲突方法之探测散列:
#include <stdio.h> #include <stdlib.h> #include <string.h> // #include <windows.h> #include <time.h> #define MAXSIZE 20 //电话薄记录数量 #define MAX_SIZE 20 //人名的最大长度 #define HASHSIZE 53 //定义表长 #define SUCCESS 1 #define UNSUCCESS -1 #define LEN sizeof(HashTable) typedef int Status; typedef char NA[MAX_SIZE]; typedef struct{//记录 NA name; NA tel; NA add; }Record; typedef struct{//哈希表 Record *elem[HASHSIZE]; //数据元素存储基址 int count; //当前数据元素个数 int size; //当前容量 }HashTable; Status eq(NA x,NA y) {//关键字比较,相等返回SUCCESS;否则返回UNSUCCESS if(strcmp(x,y)==0) return SUCCESS; else return UNSUCCESS; } Status NUM_BER; //记录的个数 void getin(Record* a){//键盘输入各人的信息 printf("输入要添加的个数:\n"); scanf("%d",&NUM_BER); int i; for(i=0;i<NUM_BER;i++){ printf("请输入第%d个记录的用户名:\n",i+1); scanf("%s",a[i].name); printf("请输入%d个记录的电话号码:\n",i+1); scanf("%s",a[i].tel); printf("请输入第%d个记录的地址:\n",i+1); scanf("%s",a[i].add); //gets(str2);?????? } } void ShowInformation(Record* a)//显示输入的用户信息 { int i; for( i=0;i<NUM_BER;i++) printf("\n第%d个用户信息:\n 姓 名:%s\n 电话号码:%s\n 联系地址:%s\n",i+1,a[i].name,a[i].tel,a[i].add); } void Cls(Record* a) { printf("*"); system("clear"); } long fold(NA s) {//人名的折叠处理 char *p; long sum=0; NA ss; strcpy(ss,s);//复制字符串,不改变原字符串的大小写 // strupr(ss);//将字符串ss转换为大写形式 p=ss; while(*p!='\0') sum+=*p++; printf("\nsum====================%d",sum); return sum; } int Hash1(NA str) {//哈希函数 long n; int m; n = fold(str);//先将用户名进行折叠处理 m = n%HASHSIZE; //折叠处理后的数,用除留余数法构造哈希函数 return m; //并返回模值 } int Hash2(NA str) {//哈希函数 long n; int m; n = atoi(str);//把字符串转换成整型数. m = n % HASHSIZE; return m; //并返回模值 } Status collision(int p,int c) {//冲突处理函数,采用二次探测再散列法解决冲突 int i,q; i=c/2+1; while(i<HASHSIZE){ if(c%2==0){ c++; q=(p+i*i)%HASHSIZE; if(q>=0) return q; else i=c/2+1; } else{ q=(p-i*i)%HASHSIZE; c++; if(q>=0) return q; else i=c/2+1; } } return UNSUCCESS; } void benGetTime(); void CreateHash1(HashTable* H,Record* a) {//建表,以人的姓名为关键字,建立相应的散列表 //若哈希地址冲突,进行冲突处理 benGetTime(); int i,p=-1,c,pp; for(i=0;i<NUM_BER;i++) { c=0; p=Hash1(a[i].name); pp=p; while(H->elem[pp]!=NULL) { pp=collision(p,c); if(pp<0) { printf("第%d记录无法解决冲突",i+1);//需要显示冲突次数时输出 continue; }//无法解决冲突,跳入下一循环 } H->elem[pp]=&(a[i]); //求得哈希地址,将信息存入 H->count++; printf("第%d个记录冲突次数为%d。\n",i+1,c);//需要显示冲突次数时输出 } printf("\n建表完成!\n此哈希表容量为%d,当前表内存储的记录个数为%d.\n",HASHSIZE,H->count); benGetTime(); } void SearchHash1(HashTable* H,int c) {//在通讯录里查找姓名关键字,若查找成功,显示信息 //c用来记录冲突次数,查找成功时显示冲突次数 benGetTime(); NA str; printf("\n请输入要查找记录的姓名:\n"); scanf("%s",str); int p,pp; p=Hash1(str); pp=p; while((H->elem[pp]!=NULL)&&(eq(str,H->elem[pp]->name)==-1)) pp=collision(p,c); if(H->elem[pp]!=NULL&&eq(str,H->elem[pp]->name)==1) { printf("\n查找成功!\n查找过程冲突次数为%d.以下是您需要要查找的信息:\n\n",c); printf("姓 名:%s\n电话号码:%s\n联系地址:%s\n",H->elem[pp]->name,H->elem[pp]->tel,H->elem[pp]->add); } else printf("\n此人不存在,查找不成功!\n"); benGetTime(); } void benGetTime() { // SYSTEMTIME sys; SHSIZE; //用除留余数法构造哈希函数 // GetLocalTime( &sys ); time_t lt; /*define a longint time varible*/ lt=time(NULL);/*system time and date*/ printf(ctime(<)); /*english format output*/ // printf( "%4d/%02d/%02d %02d:%02d:%02d.%03d \n",sys.wYear,sys.wMonth,sys.wDay,sys.wHour,sys.wMinute, sys.wSecond,sys.wMilliseconds); } void CreateHash2(HashTable* H,Record* a) {//建表,以电话号码为关键字,建立相应的散列表 若哈希地址冲突,进行冲突处理 benGetTime(); int i,p=-1,c,pp; for(i=0;i<NUM_BER;i++) { c=0; p=Hash2(a[i].tel); pp=p; while(H->elem[pp]!=NULL) { pp=collision(p,c); if(pp<0) { printf("第%d记录无法解决冲突",i+1);//需要显示冲突次数时输出 continue; }//无法解决冲突,跳入下一循环 } H->elem[pp]=&(a[i]); //求得哈希地址,将信息存入 H->count++; printf("第%d个记录冲突次数为%d。\n",i+1,c);//需要显示冲突次数时输出 } printf("\n建表完成!\n此哈希表容量为%d,当前表内存储的记录个数为%d.\n",HASHSIZE,H->count); benGetTime(); } void SearchHash2(HashTable* H,int c) {//在通讯录里查找电话号码关键字,若查找成功,显示信息 c用来记录冲突次数,查找成功时显示冲突次数 benGetTime(); NA tele; printf("\n请输入要查找记录的电话号码:\n"); scanf("%s",tele); int p,pp; p=Hash2(tele); pp=p; while((H->elem[pp]!=NULL)&&(eq(tele,H->elem[pp]->tel)==-1)) pp=collision(p,c); if(H->elem[pp]!=NULL&&eq(tele,H->elem[pp]->tel)==1){ printf("\n查找成功!\n查找过程冲突次数为%d.以下是您需要要查找的信息:\n\n",c); printf("姓 名:%s\n电话号码:%s\n联系地址:%s\n",H->elem[pp]->name,H->elem[pp]->tel,H->elem[pp]->add); } else printf("\n此人不存在,查找不成功!\n"); benGetTime(); } void Save() { FILE *fp; if((fp=fopen("c:\test.txt", "w"))==NULL){ printf("\nERROR opening customet file"); } fclose(fp); } int main(int argc, char* argv[]) { int c,flag=1; HashTable *H; H=(HashTable*)malloc(LEN); int i=0; for(;i<HASHSIZE;i++) { H->elem[i]=NULL; H->size=HASHSIZE; H->count=0; } Record a[MAXSIZE]; while (1) { printf("\n ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ "); printf("\n ┃ 欢迎使用电话号码查找系统 ┃ "); printf("\n ┏〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓┓"); printf("\n ┃ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ 哈希表的设计与实现 ★ ★ ★ ★ ★ ★ ★ ★ ★ ┃ "); printf("\n ┃ 【1】. 添加用户信息 ┃"); printf("\n ┃ 【2】. 读取所有用户信息 ┃"); printf("\n ┃ 【3】. 以姓名建立哈希表(再哈希法解决冲突) ┃"); printf("\n ┃ 【4】. 以电话号码建立哈希表(再哈希法解决冲突) ┃"); printf("\n ┃ 【5】. 查找并显示给定用户名的记录 ┃"); printf("\n ┃ 【6】. 查找并显示给定电话号码的记录 ┃"); printf("\n ┃ 【7】. 清屏 ┃"); printf("\n ┃ 【8】. 保存 ┃"); printf("\n ┃ 【9】. 退出程序 ┃"); printf("\n ┃ 温馨提示: ┃"); printf("\n ┃ Ⅰ.进行5操作前 请先输出3 ┃"); printf("\n ┃ Ⅱ.进行6操作前 请先输出4 ┃"); printf("\n ┗〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓┛"); printf("\n"); printf("请输入一个任务选项>>>"); printf("\n"); int num; scanf("%d",&num); switch(num) { case 1: getin(a); break; case 2: ShowInformation(a); break; case 3: CreateHash1(H,a); /* 以姓名建立哈希表 */ break; case 4: CreateHash2(H,a); /* 以电话号码建立哈希表 */ break; case 5: c=0; SearchHash1(H,c); break; case 6: c=0; SearchHash2(H,c); break; case 7: Cls(a); break; case 8: Save(); break; case 9: return 0; break; default: printf("你输错了,请重新输入!"); printf("\n"); break; } } return 0; }
相关文章推荐
- [C++]数据结构:散列表(哈希表)、散列函数构造、处理散列冲突
- HashSet和HashMap的底层实现——哈希表、散列表
- 哈希表、散列表
- 散列表(哈希表)
- 体会大师们的智慧-散列表(哈希表)
- 散列表(哈希表)查找算法
- 哈希表(散列表)
- 散列表(哈希表)工作原理 (转)
- 数据结构—散列表(Hash table,也叫哈希表)
- 哈希表(散列表)
- 哈希表(散列表)
- 哈希表(散列表)查找
- 哈希表(散列表)原理详解
- C++ STL中哈希表Map 与 hash_map 介绍
- 哈希表(散列表)原理详解
- 十三、散列表(哈希表)
- 散列表(哈希表)
- 【数据结构】哈希表/散列表
- 散列表(哈希表)工作原理
- 哈希表(散列表)—Hash表解决地址冲突 C语言实现