文章目录
- 对哈希表的使用者一一人来说,这是一瞬间的事。哈希表运算得非常快,在计算机程序中,如果需要在一秒种内查找上千条记录通常使用哈希表(例如拼写检查器)哈希表的速度明显比树快,树的操作通常需要O(N)的时间级。哈希表不仅速度快,编程实现也相对容易。
- 哈希表也有一些缺点它是基于数组的,数组创建后难于扩展某些哈希表被基本填满时,性能下降得非常严重,所以程序虽必须要清楚表中将要存储多少数据(或者准备好定期地把数据转移到更大的哈希表中,这是个费时的过程)。
- 而且,也没有一种简便的方法可以以任何一种顺序〔例如从小到大〕遍历表中数据项。如果需要这种能力,就只能选择其他数据结构。
- 然而如果不需要有序遍历数据,井且可以提前预测数据量的大小。那么哈希表在速度和易用性方面是无与伦比的。
- 下面在哈希表在SIP电话中用于存储当前在前用户的信息(用户信息已经在初始化是保存在全局变量的一个链表中,文中有说明,在线用户需要时刻和控制台进行交互(比如通话、占线)所有要求速度快),废话不多说直接分析代码吧~
哈希表是种数据结构,它可以提供快速的插入操作和查找操作。第一次接触哈希表时,它的优点多得让人难以置信。不论哈希表中有多少数据,插入和删除(有时包括侧除)只需要接近常量的时间即0(1)的时间级。实际上,这只需要几条机器指令。
对哈希表的使用者一一人来说,这是一瞬间的事。哈希表运算得非常快,在计算机程序中,如果需要在一秒种内查找上千条记录通常使用哈希表(例如拼写检查器)哈希表的速度明显比树快,树的操作通常需要O(N)的时间级。哈希表不仅速度快,编程实现也相对容易。
哈希表也有一些缺点它是基于数组的,数组创建后难于扩展某些哈希表被基本填满时,性能下降得非常严重,所以程序虽必须要清楚表中将要存储多少数据(或者准备好定期地把数据转移到更大的哈希表中,这是个费时的过程)。
而且,也没有一种简便的方法可以以任何一种顺序〔例如从小到大〕遍历表中数据项。如果需要这种能力,就只能选择其他数据结构。
然而如果不需要有序遍历数据,井且可以提前预测数据量的大小。那么哈希表在速度和易用性方面是无与伦比的。
下面在哈希表在SIP电话中用于存储当前在前用户的信息(用户信息已经在初始化是保存在全局变量的一个链表中,文中有说明,在线用户需要时刻和控制台进行交互(比如通话、占线)所有要求速度快),废话不多说直接分析代码吧~
头文件:
#ifndef _HASH_H #define _HASH_H #define OK 1 #define ERROR 0 #define HASH_SIZE 256 typedef struct _hashNode /*采用双向链表存储*/ { struct _hashNode * next; struct _hashNode * prev; } tHashNode; tHashNode * vSipUserLocation[ HASH_SIZE ];/*槽*/ #endif
哈希表算法(字符串处理)
int hashString (const char *str) { unsigned int h=0; int len; int i; len = strlen(str); if (len <= 0 || len > HASH_SIZE) return -1; for (i = 0; i < len; i++) { h = ( h << 5 ) - h + *(str + i); } return h % HASH_SIZE; } 哈希槽的初始化 int hashInit(tHashNode **pList, unsigned int size) { if ( !pList ) return ERROR; memset(pList, '\0', size); return OK; }
插入节点 int hashAddNode ( tHashNode ** pList, tHashNode * pNode, char *hash, func pCmpFunc ) { int index; tHashNode *tempNode = NULL; int ret; if ( !pList || !pNode || !hash ) return ERROR; index = hashString( hash ) ; if ( index < 0 ) return ERROR; /* 首个节点 */ tempNode = pList [ index ] ; if( !tempNode ) { pList [ index ] = pNode; pNode->next = NULL ; pNode->prev = NULL; return OK ; } /* 遍历是否有相同的节点 函数Pcmpfunc()是比较指针是否指向相同的内存地址*/ if( pCmpFunc ) { for( ; tempNode != NULL ; tempNode = tempNode->next ) { if((ret = pCmpFunc(tempNode, pNode, 0)) == 0) break; } if ( ret == 0 ) return OK ; } /*为找到相同的节点,插入到第一个节点位置 */ tempNode = pList [ index ] ; pList [ index ] = pNode ; pNode->next = tempNode; pNode->prev = NULL; tempNode->prev = pNode; return OK ; }
关于插入节点中的参数应用,在代码中调用函数如下:
typedef struct _Callee { tHashNode pNode; char name[24]; struct _Callee *next; }Callee
Callee *p ;
p 是指向一个全局变量 (链表,存放系统设置的所有用户的信息)中的一个节点,保存用户信息(我这里进行了修改,只显示一部分内容)。而哈希表存放的是当前在线用户,当用户注册是需要加入哈希表中。
hashAddNode(vSipUserLocation, (tHashNode *)p, p->name, issamecallee)
下面是和别人沟通:
A:
这个问题我也遇到了,因为tSipCallee *p ,这个p指向的是存储tSipCalle结构的内存地址,这个地址指向的内存是一块连续的空间,比如大小是n,那么我们可以很随意的取这n个字节中的一段内容,也就是说sizeof(tHashNode)<=sizeof(Callee),把n中的前m(<=n)个字节当成tHashNode 的内容,也就是说可以把这m个字节当成一个tHashNode结构体
但是如果sizeof(tHashNode)>sizeof(Callee),那就不行了,会造成访问越界的情况
B:
定义这个结构体的时候已经包含这个 tHasgNode pNode;
太抽象了~
A:对哈,我给你画个图你就明白了
B:看来你看得挺多了,刚开始我迷糊在(tHashNode *)p ,强制类型转换,不就把P中前2个字节内容覆盖掉了!
A:
你看看这个结构,tSipCallee在内存中的存放方法,强制转换的意思就是“我”只想使用前一段字节:sizeof(tHashNode),后面是什么我不管。
通过data查找节点
tHashNode * hashGetNode(tHashNode ** pList, char *hash, func pCmpFunc, void * cmpArg) { int index; tHashNode *tempNode = NULL; if ( !pList || !hash || !pCmpFunc ) return NULL; index = hashString( hash ) ; if ( index < 0 ) return NULL; tempNode = pList [ index ] ; while (tempNode ) {/*查找参数 cmoArg是否在节点tempNode中保存) if ( pCmpFunc ( tempNode, NULL, cmpArg ) == 0 ) { break; } tempNode = tempNode->next; } return tempNode; }
获得当前列内容:
tHashNode * hashGetSlot(tHashNode ** pList, const char *hash ) { int index; if ( !pList || !hash ) return NULL; index = hashString( hash ); if( index < 0 ) return NULL; return pList[ index ]; }
删除节点
int hashRemoveNode(tHashNode ** pList, tHashNode * pNode, char *hash) { int index; if ( !pList || !pNode || !hash ) return ERROR ; index = hashString( hash ) ; if ( index < 0 ) return ERROR; if(!pNode->prev) { /* 第一个节点 */ pList[index] = pNode->next; if(pNode->next) pNode->next->prev = NULL; }else if ( !pNode->next ) { /* 下一个节点 */ pNode->prev->next = NULL; }else { pNode->prev->next = pNode->next; pNode->next->prev = pNode->prev; } return OK; }
哈希表是处理大量数据中因处理速度比较快,应用非常广泛。但是也不难理解,只有认真阅读代码,