现在的位置: 首页 > 综合 > 正文

哈希表—-在VOIP用户信息存储中的应用

2013年09月03日 ⁄ 综合 ⁄ 共 3322字 ⁄ 字号 评论关闭
文章目录

  哈希表是种数据结构,它可以提供快速的插入操作和查找操作。第一次接触哈希表时,它的优点多得让人难以置信。不论哈希表中有多少数据,插入和删除(有时包括侧除)只需要接近常量的时间即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;		
}

哈希表是处理大量数据中因处理速度比较快,应用非常广泛。但是也不难理解,只有认真阅读代码,

 

抱歉!评论已关闭.