分享
 
 
 

编程手记之ANSI C篇-(二)哈希序列

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

编程手记之ANSI C篇-(二)哈希序列

哈希表的基本思路是:由关键码的哈希函数值来决定节点的存储地址,进而来存取节点。在我的应用中,要解决的是基于命名(字符串型关键码)来查找和存储节点,节点的内容是字符串或某个结构的指针。哈希表的结构有主散列和子序列构成,主散列是一线性数组,数组的大小为size,是一个素数,也是哈希函数的模基,数组序号[0...size]即哈希函数的值域,数组存储的是根连接件,由他维护着同义词关键码(哈希函数值相同)的节点链表。子序列是存储节点的有序序列,是按各节点关键码升序排列的,子序列主要是用于处理哈希冲撞的问题。查找的路径为,根据关键码的哈希函数值在主散列中取得根连接件,然后在此子序列中查找含该关键码的节点。

1、数据结构定义:

/*定义哈希表*/

typedef struct _HashTable{

LINK lk;/*哈希表自身连接件*/

LINKPTR pp;/*主散列线性数组指针*/

int size;/*数组大小*/

}HashTable;

/*定义节点*/

typedef struct _HashEntry{

LINK lk;/*节点自身连接件*/

TCHAR* szKey;/*关键码*/

TCHAR* szVal;/*节点字符串值*/

unsigned long data;/*节点长整型值*/

}HashEntry;

/*定义数据节点标识*/

#define lkHashEntry 1

#define lkHashTable 2

/*定义从连接件指针中恢复数据节点*/

#define HashTableFromLink(p) ((HashTable*)((unsigned int)p - (unsigned int)&(((HashTable*)0)->lk)))

#define HashEntryFromLink(p) ((HashEntry*)((unsigned int)p - (unsigned int)&(((HashEntry*)0)->lk)))

/*定义枚举哈希表节的回调函数*/

typedef int (*EnumHashEntryPtr)(TCHAR* szKey,int keylen,TCHAR* szVal,int vallen,void* pv);

2、哈希函数:

/*szKey: 关键码

keylen: 关键码长度

nPrim: 模基,一般为素数*/

static int HashCode(TCHAR* szKey,int keylen,int nPrim)

{

unsigned int sum;

int i;

assert(szKey);

assert(nPrim > 0);

/*keylen为-1指明szKey为'\0'结尾的字符串*/

if(keylen == -1)

keylen = _tcslen(szKey);

/*各字节取整求和*/

sum = 0;

for(i=0;i

{

sum += (int)(szKey[i]);

}

/*求余数*/

return sum % nPrim;

}

3、哈希表实现:

/*创建哈希表

nSize: 哈希函数的模基,即主散列数组的大小,一般取素数值*/

LINKPTR CreateHashTable(int nSize)

{

HashTable* pht;

int i;

assert(nSize > 0);

pht = (HashTable*)calloc(1,sizeof(HashTable));

pht->lk.tag = lkHashTable;

pht->size = nSize;

/*分配主散列数组空间*/

pht->pp = (LINKPTR)calloc(nSize,sizeof(LINK));

/*初始化每个根连接件*/

for(i = 0;i<size;i++)

InitRootLink(pht->pp + i);

return &(pht->lk);

}

/*销毁哈希表

ptr: 哈希表连接件指针*/

void DestroyHashTable(LINKPTR ptr)

{

HashTable* pht;

assert(ptr);

assert(ptr->tag == lkHashTable);

pht = HashTableFromLink(ptr);

/*清除所有子序列*/

ClearHashTable(ptr);

free(pht->pp);

free(pht);

}

/*清除所有子序列

ptr: 哈希表连接件指针*/

void ClearHashTable(LINKPTR ptr)

{

HashTable* pht;

HashEntry* phe;

LINKPTR plk,pre;

int i;

assert(ptr);

assert(ptr->tag == lkHashTable);

pht = HashTableFromLink(ptr);

/*清除每一个根连接件下的子序列*/

for(i=0;isize;i++)

{

plk = GetFirstLink(&(pht->pp[i]));

while(plk)

{

pre = plk;

plk = GetNextLink(plk);

phe = HashEntryFromLink(pre);

assert(phe->szKey);/*断言关键码不为空*/

free(phe->szKey);

if(phe->szVal)

free(phe->szVal);

free(phe);/*释放子节点*/

}

}

/*每个根连接件置为初始化态*/

for(i = 0;i<size;i++)

InitRootLink(pht->pp + i);

}

/*添加节点到哈希表中

ptr: 哈希表连接件指针

szKey: 关键码

keylen: 关键码长度

szVal: 值字符串

vallen: 值字符串长度*/

LINKPTR AddHashEntry(LINKPTR ptr,TCHAR* szKey,int keylen,TCHAR* szVal,int vallen)

{

HashTable* pht;

HashEntry* phe;

LINKPTR plk;

int nIndex;

assert(ptr);

assert(ptr->tag == lkHashTable);

pht = HashTableFromLink(ptr);

/*先查找是否存在同名关键码*/

plk = GetHashEntry(ptr,szKey,keylen);

if(plk != NULL)/*存在则进行值替换*/

{

phe = HashEntryFromLink(plk);

free(phe->szKey);

if(keylen == -1)

keylen = _tcslen(szKey);

phe->szKey = (TCHAR*)calloc(keylen + 1,sizeof(TCHAR));

_tcsncpy(phe->szKey,szKey,keylen);

if(phe->szVal != NULL)

{

free(phe->szVal);

phe->szVal = NULL;

}

if(szVal != NULL)

{

if(vallen == -1)

vallen = _tcslen(szVal);

phe->szVal = (TCHAR*)calloc(vallen + 1,sizeof(TCHAR));

_tcsncpy(phe->szVal,szVal,vallen);

}

}else/*否则添加新节点*/

{

phe = (HashEntry*)calloc(1,sizeof(HashEntry));

phe->lk.tag = lkHashEntry;

assert(szKey);

if(keylen == -1)

keylen = _tcslen(szKey);

phe->szKey = (TCHAR*)calloc(keylen + 1,sizeof(TCHAR));

_tcsncpy(phe->szKey,szKey,keylen);

if(szVal)

{

if(vallen == -1)

vallen = _tcslen(szVal);

phe->szVal = (TCHAR*)calloc(vallen + 1,sizeof(TCHAR));

_tcsncpy(phe->szVal,szVal,vallen);

}

/*由哈希函数值取得根连接件的位置*/

nIndex = HashCode(phe->szKey,keylen,pht->size);

plk = &((pht->pp)[nIndex]);

/*插入到该根连接件的链表中*/

OrderInsertEntry(plk,&phe->lk);

}

/*返回节点连接件指针*/

return &(phe->lk);

}

/*由关键码取得节点连接件指针

ptr: 哈希表连接件指针

szKey: 关键码

keylen: 关键码长度*/

LINKPTR GetHashEntry(LINKPTR ptr,TCHAR* szKey,int keylen)

{

int nIndex,rt;

HashEntry* phe;

HashTable* pht;

LINKPTR plk;

assert(ptr);

assert(ptr->tag == lkHashTable);

if(szKey == NULL)

return NULL;

pht = HashTableFromLink(ptr);

if(keylen == -1)

keylen = _tcslen(szKey);

/*由哈希函数值取得根连接件*/

nIndex = HashCode(szKey,keylen,pht->size);

plk = GetFirstLink(&((pht->pp)[nIndex]));

while(plk != NULL)

{

phe = HashEntryFromLink(plk);

rt = _tcsncmp(phe->szKey,szKey,keylen);

if(rt > 0)

return NULL;/*升序的后续节点中已无匹配的节点*/

else if(rt == 0 && (int)_tcslen(phe->szKey) == keylen)

break;/*两字符串的长度也应相等才匹配*/

plk = GetNextLink(plk);

}

return plk;

}

/*删除节点

elk: 节点连接件指针*/

void DeleteHashEntry(LINKPTR elk)

{

HashEntry* phe;

assert(elk);

assert(elk->tag == lkHashEntry);

phe = HashEntryFromLink(elk);

/*从根连接件链表中删除*/

assert(DeleteLinkAt(GetRootLink(elk),elk) == elk);

/*释放节点*/

free(phe->szKey);

if(phe->szVal)

free(phe->szVal);

free(phe);

}

/*取得节点的关键码字符串指针

elk: 节点连接件指针*/

TCHAR* GetHashEntryKeyPtr(LINKPTR elk)

{

HashEntry* phe;

assert(elk);

assert(elk->tag == lkHashEntry);

phe = HashEntryFromLink(elk);

return phe->szKey;

}

/*取得节点值的字符串指针

elk: 节点连接件指针*/

TCHAR* GetHashEntryValPtr(LINKPTR elk)

{

HashEntry* phe;

assert(elk);

assert(elk->tag == lkHashEntry);

phe = HashEntryFromLink(elk);

return phe->szVal;

}

/*设置节点的长整数值

elk: 节点连接件指针*/

void SetHashEntryData(LINKPTR elk,unsigned long data)

{

HashEntry* phe;

assert(elk);

assert(elk->tag == lkHashEntry);

phe = HashEntryFromLink(elk);

phe->data = data;

}

/*取得节点的长整数值

elk: 节点连接件指针*/

unsigned long GetHashEntryData(LINKPTR elk)

{

HashEntry* phe;

assert(elk);

assert(elk->tag == lkHashEntry);

phe = HashEntryFromLink(elk);

return phe->data;

}

/*枚举哈希表中的节点

ptr: 哈希表的连接件指针

pf: 调用者的回调函数

pv: 调用者的回传参数*/

int EnumHashEntry(LINKPTR ptr,EnumHashEntryPtr pf,void* pv)

{

HashTable* pht;

HashEntry* phe;

LINKPTR plk;

int i;

assert(ptr);

assert(ptr->tag == lkHashTable);

assert(pf);

pht = HashTableFromLink(ptr);

for(i=0;isize;i++)

{

plk = GetFirstLink(&((pht->pp)[i]));

while(plk != NULL)

{

phe = HashEntryFromLink(plk);

if((*pf)(phe->szKey,((phe->szKey)? _tcslen(phe->szKey) : 0),phe->szVal,((phe->szVal)? _tcslen(phe->szVal) : 0),pv))

return 1;/*如果调用者回调函数返回非零值则终止枚举*/

plk = GetNextLink(plk);

}

}

return 0;

}

/*将节点插入升序的链表

root: 根连接件指针

ptrEntry: 节点连接件指针*/

void OrderInsertEntry(LINKPTR root,LINKPTR ptrEntry)

{

HashEntry* pnew;

HashEntry* phe;

LINKPTR plk;

assert(root && root->tag == lkRoot);

assert(ptrEntry && ptrEntry->tag == lkHashEntry);

pnew = HashEntryFromLink(ptrEntry);

plk = GetFirstLink(root);

while(plk)

{

phe = HashEntryFromLink(plk);

if(_tcscmp(phe->szKey,pnew->szKey) > 0)

break;/*新节点应为当前plk的前驱*/

plk = GetNextLink(plk);

}

if(plk == NULL)

InsertLinkAt(root,LINK_LAST,ptrEntry);

else

InsertLinkBefore(plk,ptrEntry);

}

3、哈希表的基本算法分析:假设有N个节点插入到哈希表,哈希函数的模基为M,则负载因子D=N/M,由于该查找包括两步,查找主散列位置(检索长度为1)和子序列顺序查找(平均检索长度为D/2),可见该哈希表的平均检索长度为1+D/2。但我不满意的地方是在子序列中顺序表查找的中字符串比较是比较费时的,如果您有好的ideas请告诉吧!jdhot2003@hotmail.com

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有