C实现哈希表

      摘要: 使用C语言、数组与链表的组合实现哈希表数据结构。 可以学习链表操作及C文件组织。

      难度: 初级。

 

        哈希表是数组、链表及数学算法的一个综合应用, 无论在理论上还是实践上都是非常有价值的。 废话不多说了, 贴上自己的实现吧,以供参考,有错误之处,恳请指出。 还需要做的一个工作是, 为实现中用到的链表和哈希表设计详尽苛刻的测试。

        该哈希表包括四个文件: common.h , LinkList.c, Hashtable.c , main.c . 将它们放在同一个文件夹中,按照下面的方式编译运行

        $ gcc -Wall *.c -o main

        $ main

        或者 一个简单的 makefile 文件:      

OBJS = Hashtable.o main.o LinkList.o
mhash: $(OBJS)
cc -o mhash $(OBJS)
HashSearch.o: Hashtable.c common.h
cc -c Hashtable.c
main.o: main.c common.h
cc -c main.c
LinkList.o: LinkList.c common.h
cc -c LinkList.c  
.PHONY: clean
clean:
-rm $(OBJS) mhash

  

         $ make

         $ mhash

         下面是各个文件的具体实现: 

         common.h     

/*
 * common.h
 * 关于哈希查找和链表结构的声明和定义。
 *
 * 哈希查找法: 
 * 采用字符串散列函数的哈希函数和链地址法的冲突解决方案
 *
 */

enum { OK = 1, ERROR = 0, TRUE = 1, FALSE = 0, FAILURE = 1, LEN_OF_TABLE = 13, RECORD_NUM = 95 }; 

typedef struct {          /* 记录类型 */
         char  *key;
     int   value; 
} Record;

typedef struct node *LLNptr;

typedef struct node {     /*  链表结点类型 */
     Record  data;
     LLNptr  next;  
} LLNode;

typedef int  Status;

/* 单链表的函数声明,主要依照严蔚敏老师《数据结构C语言版》中的描述 */
 
Status  InitList(LLNptr *list);              /* 创建带头结点的单链表 */    
int     IsEmpty(LLNptr list);                /* 判断单链表是否为空   */
int     ListLength(LLNptr list);             /* 返回链表长度,即记录的数目(不含头结点) */
Status  GetRecord(LLNptr list, int i, Record *e);      /* 返回链表中第 i 个位置上的结点,并保存在 e 中 */
Status  ListInsert(LLNptr list, int i, Record e);      /* 将记录 e 插入到链表 list 中的第 i 个位置上 */
Status  ListDelete(LLNptr list, int i, Record *e);     /* 将链表 list 中的第 i 个位置上的记录删除,并用 e 返回 */
Status  ListTraverse(LLNptr list, void (*visit)(Record *e));  /* 使用 visit 遍历链表 */
Status  ListPrint(LLNptr list);                        /* 打印链表内容 */      
void  printRecord(Record *e);                          /* 打印记录内容 */
int   Equal(Record *e1, Record *e2);                   /* 判断记录的相等性 */

/* 返回链表 list 中与 e 满足 compare 关系的记录的指针; 如果不存在,返回 NULL */
Record*  PLocateRecord(LLNptr list, Record e, int (*compare)(Record *e1, Record *e2));

/* 哈希查找的函数声明  */

int hash(char* key);     /* 计算关键字的哈希值 */
Status InitTable(LLNptr hashtable[]);    /* 初始化哈希表 hashtable */
Status HashInsert(LLNptr hashtable[], Record e);   /* 将记录插入哈希表 hashtable 中 */
Record* HashSearch(LLNptr hashtable[], Record e);      /* 根据记录关键字查找:若有则返回指向该记录的指针,否则返回 NULL */
Status HashTraverse(LLNptr hashtable[]);           /* 遍历哈希表 */

 LinkList.c

   

/*  LinkList.c  */
/*  带头结点的单链表实现,头结点存储表长信息  */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"

Status  InitList(LLNptr* list)
{ 
     LLNptr head = (LLNptr)malloc(sizeof(LLNode));
     if (!head) {
               fprintf(stderr, "Error: fail to allocate memory!\n"); 
               return ERROR;
         }         
     head->data.value = 0;      /* 头结点的 value 域存储表长 */  
         head->data.key = NULL;     /* 头结点的 key 域 未用     */  
     head->next = NULL;
         *list = head;
     return  OK;    
}  

int  IsEmpty(LLNptr list)
{
    return list->data.value == 0;
}

int  ListLength(LLNptr list)
{
    return list->data.value;
}

Status GetRecord(LLNptr list, int i, Record *e)
{
      /* 取得表list中的第i个元素, 并用e返回 */
      
      LLNptr p = list->next;   /* p 指向第一个记录结点 */
      
          if (IsEmpty(list)) {
           fprintf(stderr, "Error: list empty!\n");
           return  ERROR;
      }
      if (i < 1 || i > ListLength(list)) {
           fprintf(stderr, "Error: position parameter %d out of range!\n", i);
           return  ERROR;
      }
      while(--i) p = p->next;  /* p 指向第i个元素 */
      *e = p->data;
      return OK;   
}

Record* PLocateRecord(LLNptr list, Record e, int (*compare)(Record *e1, Record *e2))
{
      
      /* 返回表list中与给定元素e满足compare关系的记录指针 */
      
      LLNptr p = list->next;
       
      while (p) {
         if (compare(&p->data, &e))
            return &p->data;
         p = p->next;
      }
      return NULL;      
}    

Status  ListInsert(LLNptr list, int i, Record e)
{
      /* 将记录 e 插入表list中的第 i 个位置上 */
    
      LLNptr s, p = list;
      int j = 0;    /* j 作计数器 */
      
          if (i < 1 || i > ListLength(list)+1) {
           fprintf(stderr, "Error: position parameter %d out of range!\n", i);
           return  ERROR;
      }
      while (p && j < i-1) { p = p->next; j++; }
      s = (LLNptr)malloc(sizeof(LLNode));
      if (!s) {
            fprintf(stderr, "Error: fail to allocate memory!\n");
            return ERROR;
      }
      s->data = e; s->next = p->next;
      p->next = s;
      list->data.value++;
      return OK;
}

Status  ListDelete(LLNptr list, int i, Record *e)
{
      /* 删除表list中的第i个位置上的记录,并用 e 返回 */
      
      LLNptr p1 = list;
      LLNptr p2;
      int j = 0;  /* j 作计数器 */
      
      if (IsEmpty(list)) {
           printf("Error: list empty!\n");
           return  ERROR;
      }
      if (i < 1 || i > ListLength(list)) {
           printf("Error: invalid index!\n");
           return  ERROR;
      }
      while (p1->next && j < i-1) { /* p1 指向第 i-1 个元素 */
            p1 = p1->next;
            j++;
      }
      p2 = p1->next;         /* p2 指向第 i 个元素 */
          *e = p2->data;
          p1->next = p2->next;
          free(p2);
          list->data.value--;
      return OK;
      
}

Status  ListTraverse(LLNptr list, void (*visit)(Record *e))
{
    /* 用visit函数遍历表list中的元素有且仅有一次 */
    
     LLNptr p = list->next;
     
     if (IsEmpty(list)) {
           printf("list empty!\n");
           return  ERROR;
      }
     while (p) {
          visit(&p->data);
          p = p->next;
     }
     return OK;
}

Status ListPrint(LLNptr list)
{
    return ListTraverse(list, printRecord);
}

void  printRecord(Record *e)
{
     printf("(%s, %d)\n", e->key, e->value);
}

int   Equal(Record *e1, Record *e2)
{
     return strcmp(e1->key,e2->key)==0;
}

 

     Hashtable.c

     

// Hashtable.c
// 哈希函数的实现

#include <stdio.h>
#include "common.h" 

int hash(char* key)
{
     char *p = key;
         int hash = 17;
         while (*p) {
            hash = hash * 37 + (*p);
            p++;
         }
         printf("key = %s\thash = %d , hash %% %d = %d\n" , key, hash, LEN_OF_TABLE, hash % LEN_OF_TABLE);
     return hash % LEN_OF_TABLE;
}

Status InitTable(LLNptr table[])
{
     int i;
     for (i = 0; i < LEN_OF_TABLE; i++) {
        if (!InitList(&table[i])) {
                return ERROR;
            }
     }
     return OK;
}

Status HashInsert(LLNptr table[], Record e)
{      
     int t_index = hash(e.key);
         if (PLocateRecord(table[t_index], e, Equal)) {  /* 哈希表中已存在该记录 */
             printf("Record exists, nothing to do.");
             return OK;
         }
     int ins_pos = ListLength(table[t_index]) + 1;
     if (!ListInsert(table[t_index], ins_pos, e)) {
             return ERROR;
         } 
     return OK;
}

Record* HashSearch(LLNptr table[], Record e)
{  
     int t_index = hash(e.key);
     return PLocateRecord(table[t_index], e, Equal);
}

Status HashTraverse(LLNptr table[])
{
    int i;
        printf("The hash table:\n");
    for (i = 0; i < LEN_OF_TABLE; i++) {
           printf("List[%d]:\n", i); 
       ListPrint(table[i]);
       printf("\n");
    }
    return OK; 
}

   main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"

char* toString(int i);

int main()
{
         printf("Program start up ...\n");
        
     /* 表长为LEN_OF_TABLE的哈希表,每个元素为一个单链表指针 */
         LLNptr hashtable[LEN_OF_TABLE];
         
         Record rds[RECORD_NUM];
         Record* pe;
         Record* qe;
     int i;

     if (!InitTable(hashtable)) {
        fprintf(stderr, "Error: fail to initialize the hashtable!\n");
        exit(FAILURE);
     }

         printf("initialize the hashtable successfully!\n");
         HashTraverse(hashtable);
  
         for (i = 0; i < RECORD_NUM; i++) {
              rds[i].key = toString(i);
              rds[i].value = i;
              printf("Record: (%s %d) \n", rds[i].key, rds[i].value);
              printf("prepare to insert ...");
              if (!HashInsert(hashtable, rds[i])) {
                 printf("failed to insert record!");
              }    
         }
     HashTraverse(hashtable);

         pe = (Record* ) malloc(sizeof(Record));
         pe->key = "11111";
         qe = HashSearch(hashtable, *pe);
         if (qe != NULL) {
            printRecord(qe);
         }
         else {
            printf("Record Not Found.\n");
         }
         
         pe->key = "11112";
         qe = HashSearch(hashtable, *pe);
         if (qe != NULL) {
            printRecord(qe);
         }
         else {
            printf("Record Not Found.\n");
         } 

     return 0;
}

char* toString(int i)
{
   char *p = (char *)malloc(5*sizeof(char) + 1);
   char *q;
   if (!p) {
      fprintf(stderr, "Error, fail to allocate memory.");
      exit(FAILURE);
   }
   q = p;
   while (q < p+5) {
       *q = i + ' ';
       q++;   
   }
   *q = '\0';
   return p;
}

 

  

posted @ 2011-07-06 11:18  琴水玉  阅读(436)  评论(0编辑  收藏  举报