散列:Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
简而言之,散列是一种用于以常数平均时间执行插入、删除和查找的技术。但是元素间排序将不会得到支持。可以把散列理解为介于链表与二叉树之间的数据结构。
散列最主要的事情是确定散列函数,以及解决冲突问题。(当两个关键字散列到同一个值的时候成为冲突)
解决冲突的方法最简单的有两种:分离链接法 和 开放定址法。
分离链接法的做法是将散列到同一个值的所有元素保留到一个表中,表有表头。如图:
假设关键字是前10个完全平方数并设散列函数是 : Hash( X ) = X mod 10 。
详细的代码实现如下:
Head.h
#ifndef HEAD_H_ #define HEAD_H_ #include <stdio.h> #include <stdlib.h> #include"string.h" #define MinTableSize (10) typedef char * ElementType; typedef unsigned int Index; typedef struct ListNode { ElementType data; struct ListNode *next; }*Position; typedef Position List; typedef struct HashTbl { int TableSize; //数组中链表的个数 List *TheLists; //指针数组。TheLists域是一个指向 ListNode 结构的指针的指针 }*HashTable; //这是哈希表主结构,*TheLists是一个指针数组,数组的每一项都链接一个上面的ListNode,而这个入口地址相当于链表头节点 HashTable InitializeTable(int TableSize); void DestroyTable( HashTable H); Position Find( ElementType key , HashTable H); void Insert( ElementType key , HashTable H); ElementType Retrieve( Position P); void Delete( ElementType Key, HashTable H ); #endif /* HEAD_H_ */
#include"Head.h" //unsigned long NextPrime(unsigned long n) //{ // return n/2+1; //} /* 求下一个素数 ; assume N >= 10 */ static int NextPrime( int N ) { int i; if( N % 2 == 0 ) N++; for( ; ; N += 2 ) { for( i = 3; i * i <= N; i += 2 ) { if( N % i == 0 ) goto ContOuter; /* Sorry about this! */ } return N; ContOuter: ; } } /* * 初始化函数 */ HashTable InitializeTable(int TableSize) { HashTable H; int i; if( TableSize < MinTableSize) { puts("Table size too small \n"); return NULL; } //分配表空间 H=(HashTable)malloc(sizeof(struct HashTbl)); if(H==NULL) { puts("Out of space \n"); return NULL; } H->TableSize = NextPrime(TableSize); //分配链表数组空间,即为数组整体分配空间 H->TheLists=malloc(sizeof(List) * H->TableSize); if(H->TheLists == NULL) { puts("Out of space \n"); return NULL; } //分配表头空间,即把 数组空间分割为 多个节点大小的空间 for(i=0;i < H->TableSize;i++) { H->TheLists[i] = malloc(sizeof(struct ListNode)); if(H->TheLists[i] == NULL) { puts("Out of space \n"); return NULL; } else H->TheLists[i]->next=NULL; } return H; } /* * 散列函数 */ Index Hash( const char *key , int TableSize) { unsigned int HashVal = 0; while( *key != '\0') HashVal = (HashVal << 5) + *key++; return HashVal % TableSize; } Position Find( ElementType key , HashTable H) { Position P; List L; L = H->TheLists[Hash(key,H->TableSize)]; P=L->next; while( P != NULL && strcmp(P->data,key)!=0) P = P->next; return P; } void Insert( ElementType key , HashTable H) { Position Pos,NewCell; List L; Pos = Find(key,H); // 如果没找到 if(Pos == NULL) { NewCell = malloc(sizeof(struct ListNode)); if( NewCell == NULL ) { puts("Out of space \n"); return; } else { // 从表的前端插入 L = H->TheLists[Hash(key,H->TableSize)]; NewCell->next = L->next; // 此处插入的是字符串 NewCell->data = malloc(strlen(key) + 1); strcpy(NewCell->data,key); NewCell->data[strlen(key)]='\0'; L->next = NewCell; } } } void Delete( ElementType key, HashTable H ) { Position Pos,Pre; List L; Pos = Find(key,H); if(Pos == NULL) return; else { L = H->TheLists[Hash(key,H->TableSize)]; for(Pre = L; Pre->next != NULL; Pre=Pre->next) { if(Pre->next == Pos) { Pre->next = Pos->next; free(Pos->data); free(Pos); break; } } } } ElementType Retrieve( Position P ) { return P->data; } void DestroyTable( HashTable H ) { int i; Position P; Position Tmp; for( i = 0; i < H->TableSize; i++ ) { P= H->TheLists[ i ]; while( P != NULL ) { Tmp = P->next; free( P ); P = Tmp; } } free( H->TheLists ); free( H ); }
#include"Head.h" int main(void) { HashTable table; int i; List L; Position pos; table = InitializeTable(17); Insert( "10", table ); Insert( "20", table ); Insert( "30", table ); Insert( "40", table ); Insert( "50", table ); Insert( "60", table ); Insert( "70", table ); Insert( "80", table ); Insert( "90", table ); Insert( "91", table ); Insert( "92", table ); Insert( "93", table ); Insert( "97", table ); Insert( "95", table ); Insert( "96", table ); Insert( "99", table ); // 打印数据,也可以独立写成函数 for(i=0;i<table->TableSize;i++) { printf("----hash %d ----\n",i); L = table->TheLists[i]; pos = L->next; while(pos != NULL) { printf("--%s--\n",pos->data); pos = pos->next; } } puts("\n\n\n\n"); Delete("60",table); Delete("70",table); for(i=0;i<table->TableSize;i++) { printf("----hash %d ----\n",i); L = table->TheLists[i]; if(L==NULL) break; pos = L->next; while(pos != NULL) { printf("--%s--\n",pos->data); pos = pos->next; } } DestroyTable(table); return EXIT_SUCCESS; }