c语言实现hashtable,类似C++的map和iOS的NSDictionary

来源:互联网 发布:魔侠传 网络导常检测 编辑:程序博客网 时间:2024/05/22 09:53

跟线性数组和链表不同,HashTable是快速查找的数据结构。本文中的HashTable使用链表处理数组。

该HashTable可以指定table的长度,提供了遍历的方法。包括table的长度的选择也比较讲究。

其他c的数据结构实现,dyArray参考点击打开链接  treeStruct参考点击打开链接

cp_int32 nPrime[MAX_HASH_PRIME_ARRAY_NUM] = {17,   37,   79,   163,  331,  673,  1361  };
就是说table的长度来取自上面这个数组。比如用户设定了200,那么table的长度就是331,找到第一次比输入值大的数值。可以注意到上面的都是素数。

下面是具体的代码贴出来,共大家参考。

代码中有个接口是查到多个key使用到了动态数组dyArray,参考上一篇点击打开链接

////  cpPlatform.h//  dataStruct////  Created by hherima on 14-7-29.//  Copyright (c) 2014年 . All rights reserved.//#ifndef dataStruct_cpPlatform_h#define dataStruct_cpPlatform_henum{    CP_FALSE  =   0,    CP_TRUE    =  !CP_FALSE};#define  F_MALLOC_TYPE(s) (s*)f_malloc(sizeof(s))#define  FREEFUN free#define  MIN_PRE_ALLOCATE_SIZE 10  //The initial size of the dynamic array.#define  MEMSETFUN      memset#define  REALLOCFUN     realloc#define  MALLOCFUN      malloc#define  MEMCMPFUN      memcmp#define  MEMCPYFUN      memcpytypedef unsigned char cp_bool;typedef signed int cp_int32;typedef char cp_int8;typedef unsigned int cp_uint32;#endif
上面是数据类型的定义,为了跨平台使用

下面是hashtable的数据结构头文件

////  hashStruct.h//  dataStruct////  Created by hherima on 14-7-29.//  Copyright (c) 2014年 . All rights reserved.//#ifndef dataStruct_hashStruct_h#define dataStruct_hashStruct_h#include <stdlib.h>#include "cpPlatform.h"#include "dyArray.h"struct Node{    cp_int8* m_pKey; //the hash key.    cp_int32 m_nKeyLength; // the length of the hash key in bytes.    void* m_pVal;//hash_node value    struct Node* m_pNext;};//hash table,use link list to process conflictstruct HashTable{    cp_int32 m_nLength; //hash table's length.    cp_int32 m_nEleNum; //hash table's element count.    struct Node **m_ppHead;//the hash table's array to record all key's value.};cp_int32 GetHashFileNum(cp_int8* pKey, cp_int32 nKeyLength);struct Node* HashFindOne(cp_int8* pKey,cp_int32 nKeyLength,struct HashTable *pTable);//searchcp_int32 HashFindMultkeyredund(cp_int8* pKey,cp_int32 nKeyLength,struct HashTable *pTable,struct DynamicArray *pdestArray);void HashInsertOne(cp_int8* pKey,cp_int32 nKeyLength,void* pVal,struct HashTable *pTable);//insertvoid HashInsertOneKeyredund(char* pKey,cp_int32 nKeyLength,void* pVal,struct HashTable *pTable);void HashDelOne(struct HashTable * pTable,void (*pFreeFunc) (void *),cp_int8 *pKey,cp_int32 nKeyLength);struct HashTable * HashTableCreate(cp_int32 nKeyNum);struct Node* HashGetFirst(struct HashTable *pTable);struct Node* HashGetNext(struct HashTable *pTable,cp_int8* pKey,cp_int32 nKeyLength);cp_int32 HashGetLength(struct HashTable *pTable);void HashTableVisit(struct HashTable * pTable,void (*pVisitFunc) (void *,void *),void *pAgent);void HashTableReset(struct HashTable * pTable,void (*pFreeFunc) (void *));void HashTableDestroy(struct HashTable * pTable,void (*pFreeFunc) (void *));cp_int32 HashGetValue(cp_int8* pKey,cp_int32 nKeyLength,cp_int32 nTableLength);#endif

源文件

////  hashStruct.c//  dataStruct////  Created by hherima on 14-7-29.//  Copyright (c) 2014年 . All rights reserved.//#include "hashStruct.h"#define MAX_HASH_PRIME_ARRAY_NUM    7/************************************************************************************************** function name: HashGetValue description: the hash arithmetic to get the position of the certain key. parameter: pKey: the exclusive symbol which is related to a data block in hash table. nKeyLength: the key string's length. nTableLength: the hash table's length. return value: the position of the certain key. ***************************************************************************************************/cp_int32 HashGetValue(cp_int8* pKey,cp_int32 nKeyLength,cp_int32 nTableLength){cp_uint32 h = 0;cp_uint32 g = 0;    if(!pKey || nKeyLength<1 || nTableLength<1) //if the input parameter is invalid, return.    {        return -1;    }while(nKeyLength--) // get each charactor and use it to compute hash value.{h = (h<<4) + *pKey++;g = h & 0xF0000000L;if(g){h ^= g>>24;}h &= ~g;}return h % nTableLength;}/************************************************************************************************** function name: HashFindOne description: search the position's node of the certain key. parameter: pKey: the exclusive symbol which is related to a data block in hash table. nKeyLength: the key string's length. pTable: the hash table. return value: the node pointer or null. ***************************************************************************************************/struct Node* HashFindOne(cp_int8* pKey,cp_int32 nKeyLength,struct HashTable *pTable)//search{    cp_int32 nPos = 0;struct Node* pCur = NULL;        //if the input parameter is invalid, return.if(!pTable || !pKey || nKeyLength<1)    {        return NULL;    }    nPos = HashGetValue(pKey,nKeyLength,pTable->m_nLength);    if(nPos>=0)    pCur = pTable->m_ppHead[nPos];    //cur = table->head[hash_get_value(key,key_length,table->length)];while(pCur) // if has link list, visit all the list to find the right one.{if(pCur->m_nKeyLength == nKeyLength && MEMCMPFUN(pCur->m_pKey,pKey,nKeyLength) == 0){return pCur;}pCur = pCur->m_pNext;}return NULL;}/************************************************************************************************** function name: HashFindMultkeyredund description: search the node of the certain key, there will be several one. parameter: pKey: the exclusive symbol which is related to several data block in hash table. nKeyLength: the key string's length. pTable: the hash table. pdestArray: the dynamic array which is used to save results and need be initilalized. return value: the node pointer or null. ***************************************************************************************************/cp_int32 HashFindMultkeyredund(cp_int8* pKey,cp_int32 nKeyLength,struct HashTable *pTable,struct DynamicArray *pDestArray) //search ,the key is redundant.{int nResNum = 0;struct Node* pCur = NULL;        //if the input parameter is invalid, return.if(!pTable || !pKey || nKeyLength<1 || !pDestArray)    {        return -1;    }    pCur = pTable->m_ppHead[HashGetValue(pKey,nKeyLength,pTable->m_nLength)];while(pCur) //if has link list, visit all the list and find the right one.{if(pCur->m_nKeyLength == nKeyLength && MEMCMPFUN(pCur->m_pKey,pKey,nKeyLength) == 0){DyArrayAppend(pDestArray, pCur->m_pVal); // put the right one to array.nResNum++;//return cur;}pCur = pCur->m_pNext;}return nResNum;}/************************************************************************************************** function name: hashExpand description: expand the array when it's too small.THIS FUNCTION CAN'T BE USED BECAUSE HASH VALUE IS CHANGED WHEN TABLE LENGTH CHANGED. parameter: pTable: the hash table. nNeed: the needed new size. return value: if succeed,return the true, else, return false. ***************************************************************************************************/cp_bool HashExpand(struct HashTable * pTable, cp_int32 nNeed){cp_int32 i;    struct Node ** pData = NULL;    cp_int32 nAllocSize = 0;cp_int32 nPrime[MAX_HASH_PRIME_ARRAY_NUM] = {17,   //037,   //179,   //2        163,  //3331,  //4673,  //5        1361  //6};//The prime table used to be the length of hash table to decrease conflict.        //if the input parameter is invalid, return.if(!pTable || nNeed<1)    {        return CP_FALSE;    }    //if need, expand to one and half times of original size.if(((pTable->m_nEleNum + nNeed) + ((pTable->m_nEleNum + nNeed)>>1) ) > pTable->m_nLength){nAllocSize = pTable->m_nLength + (pTable->m_nLength>>1);for(i=0; i<MAX_HASH_PRIME_ARRAY_NUM; i++) //find the nearest one of prime array.{if(nPrime[i] > nAllocSize){nAllocSize = nPrime[i];break;}}        pData = (struct Node **)REALLOCFUN(pTable->m_ppHead, sizeof(struct Node *) * nAllocSize);        if(pData != NULL)        {            // clear the expanded space.            MEMSETFUN(pData+pTable->m_nLength,0,(nAllocSize-pTable->m_nLength)*sizeof(struct Node *));            pTable->m_ppHead = pData;            pTable->m_nLength = nAllocSize;}}return ((pTable->m_nEleNum + nNeed) + ((pTable->m_nEleNum + nNeed)>>1) <= pTable->m_nLength) ? CP_TRUE : CP_FALSE;}/************************************************************************************************** function name: HashInsertOne description: insert the node of the certain key. parameter: pKey: the exclusive symbol which is related to a data block in hash table. nKeyLength: the key string's length. pVal: the node pointer of the certain key. pTable: the hash table. return value: none. ***************************************************************************************************/void HashInsertOne(cp_int8* pKey,cp_int32 nKeyLength,void* pVal,struct HashTable *pTable)//insert{cp_int32 nPos = 0;struct Node** pNode = NULL;struct Node* pTmp = NULL;        //if the input parameter is invalid, return.    if(!pTable || !pKey || nKeyLength<1)    {        return ;    }    //comment it because when it expands, the same key's position changed and the original key position CAN'T be found./*if((pTable->m_EleNum + 1) + ((pTable->m_EleNum + 1)>>1) > pTable->m_nLength)     {     HashExpand(pTable, 1);     }*///compute the hash position.pTable->m_nEleNum++;nPos = HashGetValue(pKey,nKeyLength,pTable->m_nLength);pNode = &pTable->m_ppHead[nPos];        //if this key is NOT exist, insert it.if(HashFindOne(pKey,nKeyLength,pTable) == NULL){    //malloc a new node and set the value.pTmp=(struct Node*)MALLOCFUN(sizeof(struct Node));MEMSETFUN(pTmp,0,sizeof(struct Node));pTmp->m_pKey = (char*)MALLOCFUN(nKeyLength+2);//for Unicode, two bytes zero is needed.MEMSETFUN(pTmp->m_pKey,0,nKeyLength+2);MEMCPYFUN(pTmp->m_pKey,pKey,nKeyLength);pTmp->m_nKeyLength = nKeyLength;pTmp->m_pVal = pVal;pTmp->m_pNext=NULL;        //if there is a list, then insert to the tail.while(*pNode){pNode = &((*pNode)->m_pNext);}*pNode = pTmp;}}/************************************************************************************************** function name: HashInsertOneKeyredund description: insert the node of the certain key. parameter: pKey: the symbol which is related to a data block in hash table. nKeyLength: the key string's length. pVal: the node pointer of the certain key. pTable: the hash table. return value: none. ***************************************************************************************************/void HashInsertOneKeyredund(cp_int8* pKey,cp_int32 nKeyLength,void* pVal,struct HashTable *pTable) //insert,key is redundant.{cp_int32 nPos = 0;struct Node** pNode = NULL;struct Node* pTmp = NULL;        //if the input parameter is invalid, return.    if(!pTable || !pKey || nKeyLength<1)    {        return ;    }    //comment it because when it expands, the same key's position changed and the original key position CAN'T be found./*if((table->ele_num + 1) + ((table->ele_num + 1)>>1) > table->length)     {     hash_expand(table, 1);     }*///compute the hash position.pTable->m_nEleNum++;nPos= HashGetValue(pKey,nKeyLength,pTable->m_nLength);pNode = &pTable->m_ppHead[nPos];        //compare with HashInsertOne, comment this line. if(hash_find_one(key,key_length,table) == NULL){    //malloc a new node and set the value.pTmp=(struct Node*)MALLOCFUN(sizeof(struct Node));MEMSETFUN(pTmp,0,sizeof(struct Node));pTmp->m_pKey = (char*)MALLOCFUN(nKeyLength+2);//for Unicode, two bytes zero is needed.MEMSETFUN(pTmp->m_pKey,0,nKeyLength+2);MEMCPYFUN(pTmp->m_pKey,pKey,nKeyLength);pTmp->m_nKeyLength = nKeyLength;pTmp->m_pVal = pVal;pTmp->m_pNext=NULL;        //if has a link list, then insert the new node to the tail.while(*pNode){pNode = &((*pNode)->m_pNext);}*pNode = pTmp;}}/************************************************************************************************** function name: HashTableCreate description: create the hash table when the program starts. parameter: nKeyNum: the key count of the program's data. return value: the hash table. ***************************************************************************************************/struct HashTable * HashTableCreate(cp_int32 nKeyNum){struct HashTable * pTable = NULL;cp_int32 i;cp_int32 nPrime[MAX_HASH_PRIME_ARRAY_NUM] = {17,   //037,   //179,   //2        163,  //3331,  //4673,  //5        1361  //6};//The prime table used to be the length of hash table to decrease conflict.        //if the input parameter is invalid, return.    if(nKeyNum < 1)    {        return NULL;    }pTable = (struct HashTable *)MALLOCFUN(sizeof(struct HashTable));if(!pTable)    {return NULL;    }MEMSETFUN(pTable,0,sizeof(struct HashTable));    //The default length is 1.5 times of key's number.pTable->m_nLength = nKeyNum + (nKeyNum<<1);    //find the nearest size of prime array.for(i=0; i<MAX_HASH_PRIME_ARRAY_NUM; i++){if(nPrime[i] > pTable->m_nLength){pTable->m_nLength = nPrime[i];break;}}pTable->m_ppHead = (struct Node **) MALLOCFUN(sizeof(struct Node *)*pTable->m_nLength);if(!pTable->m_ppHead){FREEFUN(pTable);return NULL;}MEMSETFUN(pTable->m_ppHead,0,sizeof(struct Node *)*pTable->m_nLength);return pTable;}/************************************************************************************************** function name: HashTableVisit description: visit all the elements of the hash table. parameter: pTable: the hash table. pVisitFunc: the visit function for each element of the hash table, the first parameter is hash's element, the second parameter is 'agent' which is to pass to caller. pAgent: used to pass to the call back funtion. return value: none ***************************************************************************************************/void HashTableVisit(struct HashTable * pTable,void (*pVisitFunc) (void *,void *),void *pAgent){cp_int32 i;struct Node * pTmp = NULL;        //if the input parameter is invalid, return.    if(!pTable)    {        return ;    }    //visit each position if it's not null.for(i=0; i<pTable->m_nLength; i++){if(pTable->m_ppHead[i] == NULL)        {            continue;        }if(pTable->m_ppHead[i]->m_pKey){if(pVisitFunc){pVisitFunc(pTable->m_ppHead[i]->m_pVal,pAgent);}pTmp = pTable->m_ppHead[i]->m_pNext;            //if has link list, then visit the whole list.while(pTmp){if(pVisitFunc){pVisitFunc(pTmp->m_pVal,pAgent);}pTmp = pTmp->m_pNext;}}}return ;}/************************************************************************************************** function name: HashGetFirst description: get the first element of the hash table. parameter: pTable: the hash table. return value: the first element ***************************************************************************************************/struct Node* HashGetFirst(struct HashTable *pTable){cp_int32 i;        //if the input parameter is invalid, return.    if(!pTable)    {        return NULL;    }    //find the first not null element of head array.for(i=0; i<pTable->m_nLength; i++){if(pTable->m_ppHead[i] == NULL)        {            continue;        }if(pTable->m_ppHead[i]->m_pKey){return pTable->m_ppHead[i];}}return NULL;}/************************************************************************************************** function name: HashGetNext description: get the next element of the current node which key is given. parameter: pTable: the hash table. pKey: the exclusive symbol which is related to a data block in hash table. nKeyLength: the key string's length. return value: the next element ***************************************************************************************************/struct Node* HashGetNext(struct HashTable *pTable,cp_int8* pKey,cp_int32 nKeyLength){    struct Node* pNext = NULL;    struct Node* pCur = NULL;    cp_int32 nPos;    cp_int32 i;        //if the input parameter is invalid, return.    if(!pTable)    {        return NULL;    }    //get the position of the key.    nPos = HashGetValue(pKey,nKeyLength,pTable->m_nLength);    if(nPos == -1)    {        return NULL;    }    pCur = pTable->m_ppHead[nPos];    if(!pCur)    {        return NULL;//the related node of the pKey is NOT exist, so return null.    }    //find the right one whose key is same with pKey.    while(pCur)    {        if(pCur->m_nKeyLength == nKeyLength && MEMCMPFUN(pCur->m_pKey,pKey,nKeyLength) == 0)        {            break;        }        pCur = pCur->m_pNext;    }    //get the next pointer directly.    if(pCur && pCur->m_pNext)    {        pNext = pCur->m_pNext;    }    else    {        //get the next not null one from the head array.        nPos++;        for(i=nPos; i<pTable->m_nLength; i++)        {            if(pTable->m_ppHead[i] == NULL)            {                continue;            }            if(pTable->m_ppHead[i]->m_pKey)            {                pNext = pTable->m_ppHead[i];                break;            }        }    }    return pNext;}/************************************************************************************************** function name: HashTableReset description: clear the hash table. parameter: pTable: the hash table. pFreeFunc: the free function for hash data "val" which is given by caller. return value: none ***************************************************************************************************/void HashTableReset(struct HashTable * pTable,void (*pFreeFunc) (void *)){cp_int32 i;struct Node * pTmp = NULL;struct Node * pTmp1 = NULL;        //if the input parameter is invalid, return.    if(!pTable)    {        return;    }    //reset each element of the head array and free related memory.for(i=0; i<pTable->m_nLength; i++){if(pTable->m_ppHead[i] == NULL)        {            continue;        }if(pTable->m_ppHead[i]->m_pKey){FREEFUN(pTable->m_ppHead[i]->m_pKey);if(pFreeFunc){pFreeFunc(pTable->m_ppHead[i]->m_pVal);}pTmp = pTable->m_ppHead[i]->m_pNext;FREEFUN(pTable->m_ppHead[i]);pTable->m_ppHead[i] = NULL;            //free all the link list.while(pTmp){FREEFUN(pTmp->m_pKey);if(pFreeFunc){pFreeFunc(pTmp->m_pVal);}pTmp1 = pTmp;pTmp = pTmp->m_pNext;FREEFUN(pTmp1);}}}    //clear the table.MEMSETFUN(pTable->m_ppHead,0,sizeof(struct Node *)*pTable->m_nLength);pTable->m_nEleNum = 0;return ;}/************************************************************************************************** function name: HashTableDestroy description: destroy the hash table when the program exits. parameter: pTable: the hash table. pFreeFunc: the free function for hash data "val" which is given by caller. return value: none ***************************************************************************************************/void HashTableDestroy(struct HashTable * pTable,void (*pFreeFunc) (void *)){cp_int32 i;struct Node * pTmp = NULL;struct Node * pTmp1 = NULL;        //if the input parameter is invalid, return.    if(!pTable)    {        return;    }    //clear each element and free its memory.for(i=0; i<pTable->m_nLength; i++){if(pTable->m_ppHead[i] == NULL)        {            continue;        }if(pTable->m_ppHead[i]->m_pKey){FREEFUN(pTable->m_ppHead[i]->m_pKey);if(pFreeFunc){pFreeFunc(pTable->m_ppHead[i]->m_pVal);}pTmp = pTable->m_ppHead[i]->m_pNext;FREEFUN(pTable->m_ppHead[i]);pTable->m_ppHead[i] = NULL;            //free the link list.while(pTmp){FREEFUN(pTmp->m_pKey);if(pFreeFunc){pFreeFunc(pTmp->m_pVal);}pTmp1 = pTmp;pTmp = pTmp->m_pNext;FREEFUN(pTmp1);}}}    //free the table.FREEFUN(pTable->m_ppHead);pTable->m_ppHead = NULL;FREEFUN(pTable);return ;}/************************************************************************************************** function name: HashDelOne description: delete one node of hash table. parameter: pTable: the hash table. pFreeFunc: the free function for hash data "val" which is given by caller. pKey: the exclusive symbol which is related to a data block in hash table. nKeyLength: the key string's length. return value: none ***************************************************************************************************/void HashDelOne(struct HashTable * pTable,void (*pFreeFunc) (void *),char *pKey,cp_int32 nKeyLength){cp_int32 nPos = 0;struct Node *pTmp = NULL;struct Node **pTmp1 = NULL;struct Node *pTmp2 = NULL;        //if the input parameter is invalid, return.    if(!pTable || !pKey || nKeyLength<1)    {        return;    }    //compute the hash position.    nPos = HashGetValue(pKey,nKeyLength,pTable->m_nLength);if(!pTable->m_ppHead[nPos])    {        return;//the related node of the key is NOT exist, return.    }    //the first one is the right one.if(pTable->m_ppHead[nPos]->m_nKeyLength == nKeyLength && MEMCMPFUN(pTable->m_ppHead[nPos]->m_pKey,pKey,nKeyLength) == 0){pTable->m_nEleNum--;FREEFUN(pTable->m_ppHead[nPos]->m_pKey);pTable->m_ppHead[nPos]->m_pKey = NULL;if(pFreeFunc)pFreeFunc(pTable->m_ppHead[nPos]->m_pVal);pTable->m_ppHead[nPos]->m_pVal = NULL;if(pTable->m_ppHead[nPos]->m_pNext){pTmp = pTable->m_ppHead[nPos];pTable->m_ppHead[nPos] = pTable->m_ppHead[nPos]->m_pNext;FREEFUN(pTmp);}else{FREEFUN(pTable->m_ppHead[nPos]);pTable->m_ppHead[nPos] = NULL;}}else{    //find the right one in the link list.pTmp1 = &(pTable->m_ppHead[nPos]->m_pNext);while(*pTmp1){if((*pTmp1)->m_nKeyLength == nKeyLength && MEMCMPFUN((*pTmp1)->m_pKey,pKey,nKeyLength) == 0){//find it, free it and relink the link list.pTable->m_nEleNum--;FREEFUN((*pTmp1)->m_pKey);(*pTmp1)->m_pKey = NULL;if(pFreeFunc)pFreeFunc((*pTmp1)->m_pVal);(*pTmp1)->m_pVal = NULL;if((*pTmp1)->m_pNext){pTmp2 = *pTmp1;*pTmp1 = (*pTmp1)->m_pNext;FREEFUN(pTmp2);}else{FREEFUN(*pTmp1);*pTmp1 = NULL;}break;}else{pTmp1 = &((*pTmp1)->m_pNext);}}}return ;}/************************************************************************************************** function name: HashGetLength description: get the total length of the hash table. parameter: pTable: the hash table. return value: none ***************************************************************************************************/cp_int32 HashGetLength(struct HashTable *pTable){    return pTable ? pTable->m_nEleNum : -1;}




1 0