zoukankan      html  css  js  c++  java
  • 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;
    }

      

  • 相关阅读:
    visual studio 目录
    CMake教程小结
    CMake教程
    blender坐标系梳理
    Blender3d obj坐标转换示意图
    行列式的向量形式
    高等代数 第一章 行列式
    C++ Union实验
    git 分支合并学习实验
    C++使用memset注意事项
  • 原文地址:https://www.cnblogs.com/lovesqcc/p/4037840.html
Copyright © 2011-2022 走看看