散列表(哈希表)介绍

来源:互联网 发布:农村淘宝怎么开通 编辑:程序博客网 时间: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
原创粉丝点击