一:散列表的定义:
散列表的实现常常叫做散列,散列是一种用于以常数平均时间执行插入,查找,删除的技术,但是,那些需要元素间任何排序信息的操作将不会得到支持,如findmin,findmax等等。散列表的优点很明显,它的查询时间为常数,速度非常快,缺点就是元素间没有排序,对于一些需要排序的场合不适用。理想的散列表数据结构就是一个包含有关键字的具有固定大小的数组,用一个散列函数来跟据关键字的值来将关键字插入到表中。这是散列的基本想法,剩下的问题是要选择一个好的散列函数,当俩个关键字散列到同一个值得时候应当如何应对以及如何确定散列表的大小。
二:选择散列函数:
1.如果输入的关键字是整数,一般的方法就是直接返回key mod tablesize;(hashval=key%tablesize;),tablesize的选择最好是素数,这是因为素数没有其他的约数。如果表的大小为10这样的数,如果关键字是以0为各位,那所有的关键字都会映射到同一位置。
2.一般散列的关键字是字符串。
一种散列函数是将字符串所有的字符的ASCII码相加。但是由于ASCII最大值只有127,所以可以映射的范围不够大。
还有一种是这样:
它假设key至少有俩个字符外加一个NULL符,它只考虑前三个字符,如果他们是随机的,那这个函数在小范围内是可以均匀分配关键字的,但是真实使用中英文字母不是随机的。所以当散列表足够大时这个函数还是不合适的。
还有一种是这样的:
这个函数一般可以分配的很好。
分离链接法:
分离连接法一种简单的散列表实现方式。做法是将散列到同一个位置的所有值
都保存到一个链表中。实现方法也比较简单,很多操作都是链表的操作。
头文件:
#ifndef __HASHTABLE_H #define __HASHTABLE_H /****************分离链表法************************/ struct ListNode; typedef struct ListNode *position; typedef position list; struct HashTable; typedef struct HashTable *hashTable; typedef int ElementType; #define MINTABLESIZE 5 #define MAXTABLESIZE 20 hashTable initializeTable(int tablesize); void destroyTable(hashTable H); position find(ElementType key,hashTable H); ElementType retrieve(position p); int nextPrime(int tableSize); int isPrime(int x); void insert(ElementType key,hashTable H); int Hash(ElementType key,int tableSize); int Delete(ElementType key,hashTable H); struct ListNode { ElementType element; position next; }; struct HashTable { int tableSize; list *thelists; //指向ListNode的指针的指针 }; /**************************************************/ #endif
实现文件:
#include "HashTable.h" #include <stdio.h> #include <stdlib.h> /***************************分离链表法****************************/ /*构造出了一个指针数组,数组的每一个元素都指向一个链表*/ hashTable initializeTable(int tableSize) { hashTable H; int i; if(tableSize<MINTABLESIZE) { printf("table is too small!\n"); return NULL; } H=(struct HashTable*)malloc(sizeof(struct HashTable)); if(H==NULL) { printf("out of space\n"); return NULL; } H->tableSize=nextPrime(tableSize); //11 //创建指针数组 H->thelists=malloc(sizeof(list)*H->tableSize); //指针数组,数组里不放数据,数据放在链表里 if(H->thelists==NULL) { printf("out of space!\n"); return NULL; } //创建指针数组元素指向的链表表头 for(i=0;i<H->tableSize;i++) { H->thelists[i]=(struct ListNode*)malloc(sizeof(struct ListNode)); if(H->thelists[i]==NULL) { printf("out of space!\n"); return NULL; } else H->thelists[i]->next=NULL; } return H; } int nextPrime(int tableSize) { while(1) { if(isPrime(tableSize)) return tableSize; else tableSize++; } } int isPrime(int x) { int i; for(i=2;i*i<=x;i++) { if(x%i==0) return 0; } return 1; } void destroyTable(hashTable H) { position h,p,q; int i; for(i=0;i<H->tableSize;i++) { h=H->thelists[i]; p=h->next; while(p) { q=p->next; if(!q) { free(p); p=NULL; } else { free(p); p=q; } } } } position find(ElementType key,hashTable H) { position p; list L; L=H->thelists[Hash(key,H->tableSize)]; p=L->next; while(p) { if(p->element==key) return p; p=p->next; } return NULL; } void insert(ElementType key,hashTable H) { position pos,newcell; list L; pos=find(key,H); if(pos==NULL) //错把pos=null当作判断条件,此条件永远为真。 { newcell=(struct ListNode*)malloc(sizeof(struct ListNode)); if(newcell==NULL) { printf("out of space\n"); exit(-1); } else { L=H->thelists[Hash(key,H->tableSize)]; newcell->next=L->next; newcell->element=key; L->next=newcell; } } } int Hash(ElementType key,int tableSize) { return key%tableSize; } int Delete(ElementType key,hashTable H) { position pos,h,L; L=H->thelists[Hash(key,H->tableSize)]; h=L->next; while(h!=NULL&&h->next&&h->next->element!=key) pos=pos->next; if(h==NULL) { printf("cant find that key!\n"); return 0; } else { pos=h->next; h->next=pos->next; free(pos); return 1; } } ElementType retrieve(position p) { return p->element; }
测试:
#include "HashTable.h" #include <stdio.h> int main() { /********************散列分离链接法测试****************************/ /* hashTable table; position p; table=initializeTable(10); insert(0,table); insert(1,table); insert(81,table); insert(4,table); insert(64,table); insert(25,table); insert(16,table); insert(36,table); insert(9,table); insert(49,table); p=find(81,table); if(p==NULL) printf("cant find 1\n"); else printf("find %d \n",p->element); if(Delete(81,table)) { printf("Delete 81\n"); } p=find(81,table); if(p==NULL) printf("cant find 81\n"); else printf("find %d \n",p->element); destroyTable(table);