散列表(哈希表)介绍
来源:互联网 发布:农村淘宝怎么开通 编辑:程序博客网 时间:2024/04/29 06:05
散列表的定义:
散列表是一种数据结构。理想的散列表数据结构是一个具有固定大小的数组。
散列函数:
对于数组的每一个对象,都会有一个关键字,我们成为键值,例如数据对象是一个字符串,就可以直接作为键值,如果数据对象是一个类,那可以取其中的某一个成员变量作为键值。将每个键值映射到从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;}//由键值获得valuetemplate<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;}//由键值查找后,删除该节点,返回该删除的节点的valuetemplate<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;}
0 0
- 散列表(哈希表)介绍
- 散列表(哈希表)的介绍与理解
- 散列表(哈希表)
- 散列表(哈希表)
- 哈希表(散列表)
- 哈希表(散列表)
- 散列表(哈希表)
- 散列表(哈希表)
- 【哈希表(散列表)】
- 哈希表(散列表)
- 散列表(哈希表)
- 散列表(哈希表)
- 哈希表(散列表)
- 哈希表(散列表)
- 哈希表(散列表)
- 哈希表(散列表)
- 散列表(哈希表)
- 散列表(哈希表)
- 中兴入局手机支付
- 生成随机字符串和验证码的类的PHP实例
- 神州手机价格曝光
- 二三维一体化 彰显GIS应急管理核心价值
- varchar 值 '2123,2385,4516' 转换成数据类型 int 时失败
- 散列表(哈希表)介绍
- html5 video format 转换
- hadoop配置 - 添加删除节点
- Linux 解压缩命令
- 控件动画效果
- Jquery 动态生成表格示例代码
- Oracle 11gR2 RAC 常用维护操作 说明
- Android ApiDemos示例解析(85):Graphics->Vertices
- UIbutton作为UIimageView的子视图 不能触发点击事件原因