符号表之四:符号表的链表实现和哈希表实现

介绍了符号表的组织形式有线性组织(重插入简洁性,轻索引性能),排序组织(插入操作耗时,重索引性能),哈希表映射(以空间换取时间)。那么如下便介绍了符号表的链表实现方式和哈希表实现方式,分别对应线性组织和哈希表映射两种方式,至于排序组织方式的实现其实只需要一个动态扩展数组便可以实现,较为简单不作讨论。网上关于符号表的实现代码有很多,没有必要重复造轮子,其中个人觉得实现最好的是erhuoxifu博主的两篇文章,代码质量很高,故而在他的代码基础上做一些讨论分析。

1. 符号表的List链表形式实现

首先来看下接口函数的设置 list.h

#ifndef _LIST_H_
#define _LIST_H_
/*
*链表实现最大的优点便是动态插入的性能很好,故而显然每个符号表项(这里称为元素项)的具体实现方式
*采用链接节点的形式最好,这样既能实现动态扩展(不盲目扩展空间),又可以很好地实现链表关联
*所以这里分为两层结构,分别定义元素项的节点和管理链表的组织信息头结构体
*/
typedef struct ListNode
{
    char*  key;
    void*  value;
    struct  ListNode* pre;
    struct  ListNode* next; 
} ListNode;

typedef struct List 
{
    struct ListNode*  head_node;
    struct ListNode*  tail_node;
    unsigned long uiNodeLength;
} List;
typedef struct List* pList;

/*
*如果是用C++编译器编译,那么接口函数为了可以被用在C语言中,声明采用C语言的函数修饰规则,
**否则链接过程中可能会找不到正确的函数名
*/
#ifdef __cplusplus  
extern "C" {  
#endif

    pList List_new( void );  

    /*
    *这里需要讨论的问题是,元素项节点结构体中存放着指向符号名key、元素值value的指针,那么在释放时
    *符号名Key的空间释放我们是可以理解的,那么元素值value是否也需要在这里释放呢?
    *思考了一下,觉得还是不应该在List_free中释放具体的元素项的value指针,有两点考虑
    *1.不符合“谁制造,谁负责”的原则,这样容易出现模块之间功能界限模糊
    *2.元素项中存放具体元素值时,因为不知道元素值的类型,而采用通用指针void*,那么显然在不知道元素值类
    *型的情况下,是无法正确释放该元素值所占用的空间的,虽然让用户自己负责自己的值空间的分配和释放确实容易
    *出现忘记释放的情况,但是如果将该工作转移到symbol_list库中来实现,工作量和难度都会增加不少,
    *故而基于以上考虑,放弃在List_free和List_remove中对具体元素项的值空间的释放
    */
    void List_free( pList oList );

    int List_getLength( pList oList );

    /*
    *这里要讨论的是List_put如何遇到要存储元素已经存在,该如何面对,是更新,还是不更新?
    *我们知道在有些用户使用性友好的库中,这种插入操作有时是和replace混用的,但这要求库的设计者对
    *库的使用场景足够熟悉,才能做出这种果断的融合操作的
    *显然出于功能界限尽可能清晰的原则考虑,这里我还是认为不应该出现put插入和replace替换混用
    */
    int List_put( pList oList, const char* pcKey, const void* pvValue );
    /*
    *和上面List_put同样的道理,既然是replace替换,所以就应该是要替换的元素已经存在在List中,
    *不应该增加:如果要替换的元素此前不存在,那么便将该元素项作为新插入项插入List中这种功能组合的设计
    */
    void* List_replace( pList oList, const char* pcKey, const void* pvValue );

    int List_contains( pList oList, const char* pcKey );

    /*
    *出于功能安全的角度考虑,库传出来给用户的值尽可能应该是传值、传bool这类的直接量,而不应该传出
    *元素项指针,链表组织信息(如头结点地址)这类敏感的信息的指针
    *所以这里的List_get应该完成的操作是根据给定的符号名来给出元素项具体值空间,前面又说道了List库
    *是不对具体的元素值空间负责的,所以这里只能传出值空间首地址,让程序上层调用者来负责那些指针安全操作的
    *内容,List库的原则应该是自己产生的指针信息绝对不外泄
    */
    void* List_get( pList oList, const char* pcKey );

    /*参考上面关于List_free的描述*/
    void* List_remove( pList oList, const char* pcKey );

    /*
    *给List链表设计一个类似于lambda函数遍历操作的接口,结合了lambda函数+迭代器的原则,
    *功能目标是在List链表中将所有元素项调用传递的pfApply函数指针指向的函数调用一遍
    */
    void  List_map( pList oList,
                    void (*pfApply)(const char* pcKey, const void* pvValue, void* pvExtra),
                    const void* pvExtra );
#ifdef __cplusplus
}
#endif

#endif  //end of _LIST_H_

来看下具体的链表接口函数实现,我对原代码进行了一点更改,采用双向链表以加速删除这类操作的性能
list.cpp

#include "list.h"  //“”意味着优先在当前目录下搜索库
#include <stdio.h> //<>意味着按照#include环境变量设置的绝对路径进行搜索
#include <stdlib.h>
#include <assert.h>
#include <string.h>

/* 
* Use calloc to allocate the memory, 
* because it will initialize all memory bits to zero. 
* calloc函数相比于malloc函数最大的不同便是初始化操作,前者带有分配空间全部清空的操作
*/  
static void* MyAlloc ( unsigned long size )  
{  
    void* tmp;  

    tmp = ( void* ) calloc ( size, sizeof ( char ) );  
    assert( tmp );  

    return tmp;  
}  

pList List_new( void )
{
    return (pList)MyAlloc(sizeof(List));
}

void List_free( pList oList )
{
    assert( oList );
    ListNode* temp = oList->head_node;

    while ( temp )
    {
        oList->head_node = oList->head_node->next;

        if ( temp->key )
            free( temp->key );
        //if ( temp->value )  //value的释放不应该由List内部来实现,应该遵循着谁制造,谁负责的逻辑
        //  free( temp->value );
        free( temp );

        temp = oList->head_node;
    }
    free( oList );
    oList = NULL;
}

int List_getLength( pList oList )
{
    return oList->uiNodeLength;
}

/*
*static修饰本函数仅限于本文件list.cpp内部使用,之所以加这一个函数是为了内部其他函数方便获取具体元素
*的指针信息,从而方便操作,但是该函数显然是不能向外部开放的
*/
static ListNode* List_getKey( pList oList, const char* pcKey )
{
    assert( oList );
    assert( pcKey );

    if (strlen(pcKey) == 0)
        return NULL;

    if ( oList->uiNodeLength == 0)
        return NULL;

    ListNode* temp = oList->head_node;
    while (temp)
    {
        if( strcmp( pcKey, temp->key) == 0)
            return temp;
        temp = temp->next;
    }

    return NULL;
}

//插入元素项,如果元素项存在,那就意味着本次插入不成功,返回0,成功返回1  
int List_put( pList oList, const char* pcKey, const void* pvValue )
{
    assert( oList );
    assert( pcKey );
    assert( pvValue);

    if ( strlen( pcKey) == 0)
        return 0;
    if ( List_getKey( oList, pcKey) )
        return 0;

    else
    {
        ListNode* new_node = (ListNode*)MyAlloc( sizeof(ListNode) );
        assert( new_node );

        char* key = (char*)MyAlloc( strlen(pcKey) + 1);
        if (key == NULL)
        {
            free( new_node);
            return 0;
        }

        new_node->key = key;
        strcpy( new_node->key, pcKey );
        new_node->value = (void*)pvValue;

        if (oList->uiNodeLength == 0)
        {
            oList->head_node = new_node;
            oList->tail_node = new_node;
            new_node->pre = NULL;
            new_node->next = NULL;
            oList->uiNodeLength = 1;

            return 1;
        }
        else
        {
            new_node->pre = oList->tail_node;
            new_node->next = NULL;
            oList->tail_node->next = new_node;
            oList->tail_node = new_node;
            oList->uiNodeLength += 1;

            return 1;
        }

    }
}

//根据给定的元素符号名查找该元素是否存在,如果存在返回1,否则返回0
int List_contains( pList oList, const char* pcKey )
{
    assert( oList );
    assert( pcKey );
    if ( List_getKey( oList, pcKey) )
        return 1;
    else
        return 0;
}

//元素替换接口函数的实现,替换的前提是必须列表中存在这样的元素
void* List_replace( pList oList, const char* pcKey, const void* pvValue )
{
    assert( oList );
    assert( pcKey );

    ListNode* tempListNode = List_getKey( oList, pcKey );

    if (tempListNode == NULL)
        return NULL;
    else
    {   
        void* tempP = tempListNode->value;
        tempListNode->value = (void*) pvValue;
        return tempP;
    }
}
//根据具体元素的符号名查找元素项,但是显然不能返回该元素项的指针,而只能返回值空间的首地址,
void* List_get( pList oList, const char* pcKey )
{
    assert( oList );
    assert( pcKey );

    if (strlen(pcKey) == 0)
        return NULL;
    else
    {
        ListNode* temp = List_getKey( oList, pcKey );
        return temp ? temp->value : NULL;
    }
}   

//删除指定符号名的元素项,并返回元素原值
void* List_remove( pList oList, const char* pcKey )
{
    assert( oList );
    assert( pcKey );

    if ( strlen(pcKey) == 0)
        return NULL;
    else
    {
        ListNode* temp = List_getKey( oList, pcKey );
        void* tempValue = NULL;
        if (temp)
        {
            if ((temp != oList->tail_node) && (temp != oList->head_node))
            {
                temp->pre->next = temp->next;
                temp->next->pre = temp->pre;
            }
            else if (temp == oList->head_node )
            {
                if (oList->uiNodeLength == 1)
                {
                    oList->head_node = NULL;
                    oList->tail_node = NULL;
                }
                else
                {
                    oList->head_node = temp->next;
                    temp->next->pre = NULL;
                }
            }
            else //if (temp == oList->tail_node )
            {
                if (oList->uiNodeLength == 1)
                {
                    oList->head_node = NULL;
                    oList->tail_node = NULL;
                }
                else
                {
                    oList->tail_node = temp->pre;
                    temp->pre->next = NULL;
                }
            }

            tempValue = temp->value;

            free(temp->key);
            //free(temp->value);
            free(temp);

            oList->uiNodeLength -= 1;
        }

        return tempValue;
    }
}

void  List_map( pList oList,
                void (*pfApply)(const char* pcKey, const void* pvValue, void* pvExtra),
                const void* pvExtra )
{
    assert( oList );
    assert( pvExtra );
    assert( pfApply );

    ListNode* temp = oList->head_node;
    while (temp)
    {
        assert( temp->key );
        assert( temp->value );
        pfApply( temp->key, temp->value, (void*)pvExtra);
        temp = temp->next;
    }

    return;
}

2. 符号表的hash-table形式实现

首先来看下接口函数的设置 hash-table.h

#ifndef _HASH_TABLE_H_
#define _HASH_TABLE_H_
/*建立三级结构,分别是具体元素项、哈希单位置链、符号表,因为符号表机制采用的是诸侯鼎力的机制
*为了因对哈希冲突,本程序实现的是同一哈希key值处用链表串联来解决,而非传统的顺移方案
*/

/*建立hash-table中的元项实现,因为每个元素项的哈希索引值可能存在冲突,所以应该在元素节点中保留
*插入冲突节点的可能性,这便是引入struct ListNode* next的原因
*/
typedef struct ListNode
{
    char* key;
    void* value;
    struct ListNode* next;
}ListNode;

/*声明该链的组织信息*/
typedef struct ListAssociation
{
    struct ListNode*   head_node;
    struct ListNode*   tail_node;
    unsigned long      uiNodeLength; //指明该链长度
}ListAssociation;

typedef struct HashTable
{
    struct ListAssociation**  bucketTable;//子表的索引入口
    unsigned long      uiBindingsCount; //保存该hash-table所有有效子表的数目
    unsigned long      uiBucketCount;   //保存当前哈希表内含的子表数目的上限
} HashTable;

typedef struct  HashTable* HashTable_t;
#ifdef __cplusplus  
extern "C" {  
#endif
    /*初始化哈希表,根据给定的容量设置BucketCount,初始化BucketCount个listAssocation*指针信息*/
    HashTable_t HashTable_new( void );

    void HashTable_free( HashTable_t oHashTable );

    int HashTable_getLength( HashTable_t oHashTable );

    /*
    *往指定哈希表插入新元素,和讨论符号表的List实现时一样,既然是插入新元素,那么如果不是新元素
    *那么显然插入新元素是不存在,即插入新元素和更改元素的功能严格分离
    */
    int HashTable_put ( HashTable_t oHashTable, const char* pcKey, const void* pvValue);

    void* HashTable_get( HashTable_t oHashTable, const char* pcKey );

    /*将哈希表原有的元素项的值替换成新值pvValue,并返回旧值,如果该元素项不存在,则返回NULL*/
    void* HashTable_replace( HashTable_t oHashTable, const char* pcKey, const void* pvValue);

    /*在哈希表中,给定元素的符号名,删除该指定元素项*/
    void* HashTable_remove( HashTable_t oHashTable, cosnt char* pcKey);

    void HashTable_map( 
        HashTable_t oHashTable,
        void (*pfApply)( const char* pcKey, const void* pvValue, void* pvExtra),
        const void* pvExtra );

hash-table.cpp

#include "hash-table.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>

#define ARRAY_SIZE 3

static const unsigned long kBucketCount[ARRAY_SIZE] = {1024, 2048, 666666};
static int giBucketCountIndex = 0;

static void* MyAlloc( unsigned long size );

/*
*给定一个符号表组名称,和当前要查找的元素符号名key --pcKey,因为同样的符号名可能存在哈希冲突,故而应该
*返回的是一个单向链表的表头或链表信息节点
*/
static ListAssociation* HashTable_getListAssociation ( 
    HashTable_t  oHashTable,
    const char* pcKey );

/*给定一个冲突链条,在其中查找具体的元素节点*/
static ListNode* ListAssociation_getListNode (
    const ListAssociation* pListAssociation,
    const char* pcKey );

/*在哈希表中直接查找到具体元素项的首地址,注意static修饰符,该函数是该文件的内部函数,纯粹是为了方便
*其他函数的实现,对外部并不可见,之所以提到最前面给出函数声明,是因为该库要兼容C,所以才会有这些操作
*/
static ListNode* HashTable_getListNode(
    HashTable_t oHashTable,
    const char* pcKey );

/*非覆盖版插入操作,即插入和更新绝对不混用*/
static int ListAssociation_put( ListAssociation* pListAssociation, 
                                const char* pcKey,
                                const void* pvValue );

/*hash-table扩展操作,如果当前哈希表容量不足,则由1024->2048->666666递增,递增成功,则返回1,否则返回0*/
static int Rehash( HashTable_t oHashTable );

static int ListAssociation_put_help_rehash(
    ListAssociation* pListAssociation,
    ListNode*  pListNode );

/*获取当前哈希表的容量所处级别*/
static int GetBucketSizeIndex( unsigned long uiBucketCounts );

/*很简单的hash函数,该步操作返回的哈希映射后的下标*/
int HashTable_hash( const char* pcKey, int iBucketCount )
{
    int i;
    unsigned int uiHash = 0U;
    assert( pcKey != NULL);

    for (i=0; pcKey[i] != '\0'; i++)
    {
        uiHash = uiHash * 66666 + (unsigned int)pcKey[i];
    }

    return (int)( uiHash % (unsigned int)iBucketCount );
}

HashTable_t HashTable_new( void )
{
    HashTable_t temp = (HashTable_t) MyAlloc( sizeof(HashTable) );

    if(temp)
    {
        struct ListAssociation ** pListAssociation = NULL;
        temp->uiBucketCount = kBucketCount[0]; //初始将hash_table初始化为1024个节点
        pListAssociation = (ListAssociation**) MyAlloc(temp->uiBucketCount * sizeof( ListAssociation* ) );

        if (pListAssociation == NULL)
        {
            free(temp);
            return NULL;
        }
        temp->bucketTable = pListAssociation;
    }

    return temp;
}

void HashTable_free( HashTable_t oHashTable )
{
    assert( oHashTable );
    unsigned long uiBucketCount = oHashTable->uiBucketCount;

    //遍历删除子表的内容
    struct ListAssociation* pListAssociation = NULL;
    for (int i = 0; i<uiBucketCount; i++ )
    {
        pListAssociation = oHashTable->bucketTable[i];

        if( NULL == pListAssociation )
            continue;

        ListNode* temp = pListAssociation->head_node;

        while (temp)
        {
            pListAssociation->head_node = pListAssociation->head_node->next;

            if (temp->key)
            {
                free( temp->value );
                free( temp->key );
            }

            free(temp);
        }
        free( pListAssociation);
        pListAssociation = NULL;
    }

    free( oHashTable->bucketTable );
    free( oHashTable );

    oHashTable = NULL;
}

int HashTable_getLength( HashTable_t oHashTable )
{
    return oHashTable->uiBindingsCount;
}

int HashTable_put ( HashTable_t oHashTable, const char* pcKey, const void* pvValue)
{
    assert( oHashTable );
    assert( pcKey );
    assert( pvValue );
    assert( oHashTable->uiBucketCount ); //确保当前哈希表的子表数目不为0

    if (0 == strlen( pcKey) )
        return 0;

    if (oHashTable->uiBucketCount < kBucketCount[ARRAY_SIZE-1] )
    {
        if (oHashTable->uiBindingsCount >= oHashTable->uiBucketCount )
        {
            if( Rehash(ohashTable) == 0)
                return 0;
        }
    }

    ListAssociation* pListAssociation = HashTable_getListAssociation( oHashTable, pcKey );
    if (NULL == pListAssociation )
    {
        pListAssociation = ( ListAssociation* ) MyAlloc( sizeof(ListAssociation) );

        if (pListAssociation == NULL )
            return 0;

        oHashTable->bucketTable[HashTable_hash( pcKey, oHashTable->uiBucketCount)] = pListAssociation;
    }

    if (ListAssociation_put( pListAssociation, pcKey, pvValue) )
    {
        oHashTable->uiBindingsCount = oHashTable->uiBindingsCount + 1;
        return 1;
    }

    return 0;
}

int HashTable_contains( HashTable_t oHashTable, const char* pcKey )
{
    assert( oHashTable );
    assert( pcKey );

    if(HashTable_getListNode( oHashTable, pcKey) )
        return 1;
    else
        return 0;
}

void* HashTable_get( HashTable_t oHashTable, const char* pcKey )
{
    assert( oHashTable );
    assert( pcKey );

    ListNode* pListNode = HashTable_getListNode( oHashTable, pcKey );

    if(pListNode)
        return pListNode->value;

    return NULL;
}

//对哈希表中所有元素进行遍历操作,操作函数为pfApply指定的函数指针
void HashTable_map( 
    HashTable_t oHashTable,
    void (*pfApply)( const char* pcKey, const void* pvValue, void* pvExtra),
    const void* pvExtra )
{
    assert( oHashTable );
    assert( pfApply );
    assert( pvExtra );

    unsigned long uiBucketCount = oHashTable->uiBucketCount;
    int i = 0;

    if ( HashTable_getLength( oHashTable) == 0)
        return;

    for ( i = 0; i< uiBucketCount; i++)
    {
        ListAssociation* pListAssociation = oHashTable->bucketTable[i];
        if (pListAssociation == NULL)
            continue;

        ListNode* pListNode = pListAssociation->head_node;

        while (pListNode)
        {
            assert( pListNode->key);
            assert( pListNode->value);
            pfApply( pListNode->key, pListNode->value, (void*)pvExtra );
            pListNode = pListNode->next;
        }
    }

    return;
}

//将哈希表原有的元素项的值替换成新值pvValue,并返回旧值,如果该元素项不存在,则返回NULL
void* HashTable_replace( HashTable_t oHashTable, const char* pcKey, const void* pvValue)
{
    assert( oHashTable );
    assert( pcKey );

    void* value = NULL;
    ListNode* pListNode = HashTable_getListNode( oHashTable, pcKey);
    if ( pListNode )
    {
        value = pListNode->value;
        pListNode->value = (void*)pvValue;
    }

    return value;
}
//在哈希表中,给定元素的符号名,删除该指定元素项
void* HashTable_remove( HashTable_t oHashTable, cosnt char* pcKey)
{
    assert( oHashTable );
    assert( pcKey );

    ListAssociation* pListAssociation = HashTable_getListAssociation( oHashTable, pcKey);
    if (pListAssociation == NULL)
        return NULL;

    void* value = NULL;
    ListNode* temp = pListAssociation->head_node;
    ListNode* pre_node = NULL;

    //如果哈希后得到位置处没有有效信息,则可以直接返回
    if (temp = NULL)
        return NULL;

    //如果哈希后位置处有信息,则需要遍历该处的具体链表,查找到匹配的”符号名“
    //1. 链表首项就是要查找的元素项,则需要额外处理,因为涉及到pListAssociation->head_node等组织信息的更新
    if( strcmp(pcKey, temp->key) == 0)
    {
        value = temp->value;
        pListAssociation->head_node = temp->next;

        free(temp->value);
        free(temp->key);
        free(temp);

        pListAssociation->uiNodeLength = pListAssociation->uiNodeLength - 1;
        oHashTable->uiBindingsCount = oHashTable->uiBindingsCount - 1;

        return value;
    }

    while (1)
    {
        pre_node = temp;
        temp = temp->next;
        if (temp == NULL)
            return NULL;

        if ( strcmp(pcKey, temp->key) == 0 )
        {
            pre_node->next = temp->next;  //链条串起来不能断
            if ( temp == pListAssociation->tail_node )
                pListAssociation->tail_node = pre_node;

            value = temp->value;

            free( temp->key );
            free( temp->value);
            free( temp );

            pListAssociation->uiNodeLength = pListAssociation->uiNodeLength - 1;
            oHashTable->uiBindingsCount = oHashTable->uiBindingsCount - 1;

            return value;
        }
    }

    return NULL;
}

static void* MyAlloc( unsigned long size )
{
    void* temp = (void*) malloc( size*sizeof(char) );
    assert(temp);

    return temp;
}

//给定元素项的“符号名”,返回哈希后该元素项在哈希表中下表位置
static ListAssociation* HashTable_getListAssociation( HashTable_t oHashTable, const char* pcKey)
{
    assert( oHashTable);
    assert( pcKey);

    if ( strlen(pcKey) == 0)
        return NULL;

    int iHash_key = -1;

    iHash_key = HashTable_hash( pcKey, oHashTable->uiBucketCount );
    assert( iHash_key >= 0);

    return oHashTable->bucketTable[iHash_key];
}

//应对哈希冲突,如果同样的哈希值处存在多个元素项,则采用链表方式组织,故而针对具体的符号名需要二次操作
//1. 将符号名哈希后得到下标位置; 2.在下标位置上遍历链表进一步找到匹配的符号名项
static ListNode* ListAssociation_getListNode( 
    const ListAssociation* pListAssociation, 
    const char* pcKey)
{
    assert( pListAssociation );
    assert( pcKey );

    if (strlen(pcKey) == 0)
        return NULL;

    ListNode* pListNode = pListAssociation->head_node;
    while ( pListNode )
    {
        if ( strcmp( pcKey, pListNode->key ) == 0 )
            return pListNode;
        pListNode = pListNode->next;
    }

    return  NULL;
}

//在具体哈希值链条上插入元素,如果该元素原先存在,则直接返回0;如果不存在则新建项,并插入链表中,并返回1
static int ListAssociation_put(
    ListAssociation* pListAssociation,
    const char* pcKey,
    const void* pvValue )
{
    assert( pListAssociation );
    assert( pcKey );
    assert( pvValue );

    if (ListAssociation_getListNode( pListAssociation, pcKey ))
        return 0;
    else
    {
        ListNode* new_node = (ListNode*)MyAlloc( sizeof(ListNode) );
        assert(new_node);

        char* key = (char*)MyAlloc( strlen( pcKey) + 1);
        if (key == NULL)
        {
            free(new_node);
            return 0;
        }

        new_node->key = key;
        strcpy( new_node->key, pcKey );
        new_node->value = (void*)pvValue; 

        if ( pListAssociation->uiNodeLength == 0)
        {
            pListAssociation->head_node = new_node;
            pListAssociation->tail_node = new_node;
            pListAssociation->uiNodeLength = 1;

            return 1;
        }
        else
        {
            pListAssociation->tail_node->next = new_node;
            pListAssociation->tail_node = new_node;
            pListAssociation->uiNodeLength += 1;

            return 1;
        }
    }
}

static ListNode* HashTable_getListNode( HashTable_t oHashTable, const char* pcKey)
{
    assert( oHashTable );
    assert( pcKey );

    if (strlen(pcKey) == 0)
        return NULL;

    ListAssociation* pListAssociation = NULL;

    if (HashTable_getLength( oHashTable) == 0)
        return NULL;

    pListAssociation = HashTable_getListAssociation( oHashTable, pcKey);

    if (pListAssociation == NULL)
        return NULL;

    return ListAssociation_getListNode( pListAssociation, pcKey);
}

static int Rehash( HashTable_t oHashTable )
{
    assert( oHashTable );
    assert( oHashTable->uiBindingsCount >= oHashTable->uiBucketCount );

    giBucketCountIndex = GetBucketSizeIndex( oHashTable->uiBucketCount );
    assert( giBucketCountIndex >= 0);
    if ((ARRAY_SIZE-1) == giBucketCountIndex )
        return 1;

    giBucketCountIndex++;

    unsigned long uiBucketCounts = kBucketCount[giBucketCountIndex];

    struct ListAssociation** pNewListAssociation = NULL;
    pNewListAssociation = ( ListAssocaition**)MyAlloc( uiBucketCounts * sizeof( ListAssocaition*) );
    if (pNewListAssociation == NULL)
        return 0;

    //将原先哈希表中的内容进行整体搬迁,由于并无时间序上的要求,故而搬迁时对于不同节点的先入先出顺序并无特殊要求
    unsigned long uiOldBucketCount = oHashTable->uiBucketCount;
    unsigned long i = 0;
    for( ; i < uiOldBucketCount; i++ )
    {
        struct ListAssociation* pOldListAssociation = NULL;
        pOldListAssociation = oHashTable->bucketTable[i];

        if ( pOldListAssociation == NULL )
            continue;

        ListNode* pTempListNode = pOldListAssociation->head_node;
        while ( pTempListNode )
        {
            int iHash_key = -1;
            iHash_key = HashTable_hash( pTempListNode->key, uiBucketCounts );
            assert( iHash_key >= 0 );

            ListAssocaition* pListAssociation = pNewListAssociation[iHash_key];
            if ( pListAssociation == NULL)
            {
                pListAssociation = (ListAssocaition*)MyAlloc( sizeof(ListAssocaition) );
                if (pListAssociation == NULL)
                    return 0;

                pNewListAssociation[iHash_key] = pListAssociation;
            }

            pOldListAssociation->head_node = pOldListAssociation->head_node->next;
            if (ListAssociation_put_help_rehash( pListAssociation, pTempListNode )  == 0)
                return 0;

            pTempListNode = pOldListAssociation->head_node;
        }

        free( pOldListAssociation );
        pOldListAssociation = NULL;
    }

    free( oHashTable->bucketTable );

    //将哈希表的新的组织信息更新
    oHashTable->bucketTable = pNewListAssociation;
    oHashTable->uiBucketCount = uiBucketCounts;
    //oHashTable->uiBindingsCount代表当前有效链表数目,不变

    return 1;
}   

//该函数时配合rehash函数的,出于的目的主要是重复利用原有的元素项,而不需要重新分配
static int ListAssociation_put_help_rehash( 
    ListAssocaition* pListAssociation, 
    ListNode* pListNode )
{
    assert( pListAssociation );
    assert( pListNode );
    assert( pListAssociation->uiNodeLength >= 0 );

    //如果该链表已经存在该同符号名元素项,则返回
    if ( ListAssociation_getListNode( pListAssociation, pListNode->key) )
        return 0;
    else
    {
        //如果链表是空,则
        if (pListAssociation->uiNodeLength == 0)
        {
            pListAssociation->head_node = pListNode;
            pListAssociation->tail_node = pListNode;
            pListAssociation->tail_node->next = NULL;
            pListAssociation->uiNodeLength = 1;

            return 1;
        }
        else
        {
            pListAssociation->tail_node->next = pListNode;
            pListAssociation->tail_node = pListNode;
            pListNode->next = NULL;
            pListAssociation->uiNodeLength += 1;

            return 1;
        }
    }
}

static int GetBucketSizeIndex( unsigned long uiBucketCounts )
{
    int i = 0;

    for ( i=0; i < ARRAY_SIZE; i++)
    {
        if (uiBucketCounts == kBucketCount[i])
        {
            giBucketCountIndex = i;
            return giBucketCountIndex;
        }
    }

    return -1;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值