雜湊表是數組、鏈表及數學演算法的一個綜合應用, 無論在理論上還是實踐上都是非常有價值的。 廢話不多說了, 貼上自己的實現吧,以供參考,有錯誤之處,懇請指出。 還需要做的一個工作是, 為實現中用到的鏈表和雜湊表設計詳盡苛刻的測試。
該雜湊表包括四個檔案: common.h , LinkList.c, Hashtable.c , main.c . 將它們放在同一個檔案夾中,按照下面的方式編譯運行
$ gcc -Wall *.c -o main
$ main
或者 一個簡單的 makefile 檔案:
OBJS = Hashtable.o main.o LinkList.o
mhash: $(OBJS)
[tab]cc -o mhash $(OBJS)
HashSearch.o: Hashtable.c common.h
[tab]cc -c Hashtable.c
main.o: main.c common.h
[tab]cc -c main.c
LinkList.o: LinkList.c common.h
[tab]cc -c LinkList.c
.PHONY: clean
clean:
[tab]-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;}