符号表之Hash table 实现

如有错误请指正

接口定义请参考 点击打开链接,只有接口名字不一样,所里这里不再列出了。


接口实现

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

#define ARRAY_SIZE 3
// Store the bucket counts of hash table
static const unsigned long kBucketCount[ARRAY_SIZE] = \
{1024, 2048, 666666};
// Hold current Bucket size
static int giBucketCountIndex = 0;

/* ListNode to identify the data unit under processing */
typedef struct ListNode
{
    char*        key;     // Current node key
    void*        value;  // Pointer to the value which binding to the key
    struct ListNode* next; // Next node
} ListNode;

/** Association */
typedef struct ListAssociation
{
    struct ListNode*      head_node;
    struct ListNode*      tail_node;
    unsigned long         uiNodeLength; // The length of linked list
} ListAssociation;


/* HashTable to collect the hash table data  */
typedef struct HashTable
{
    struct ListAssociation**  bucketTable;
    unsigned long      uiBindingsCount; // Hold bindings size
    unsigned long      uiBucketCount;   // Hold Bucket size
} HashTable;

typedef struct HashTable* HashTable_t;

/* Local help function
* Use calloc to allocate the memory,
* because it will initialize all memory bits to zero.
*/
static void* MyAlloc ( unsigned long size );

/**
*Local function, if find out the pcKey return the ListAssociation*,
*other return NULL
*/
static ListAssociation* HashTable_getListAssociation(
    HashTable_t oHashTable,
    const char* pcKey );


/**
*Local function, if find out the pcKey return the ListNode*,
*other return NULL
*/
static ListNode* ListAssociation_getListNode(
    const ListAssociation* pListAssociation,
    const char* pcKey );

/**
*Local function.
*If a pcKey isn’t already in the ListAssociation pointed to by 
*pListAssociation,inserts it into the ListAssociation and returns 1.
*If not enough memory is available or pcKey already is in the 
*ListAssociation, the function will leave ListAssociation 
*unchanged and return 0.
*/
static int ListAssociation_put( ListAssociation* pListAssociation,
                                const char* pcKey,
                                const void* pvValue );


/**
*Local function, if find out the pcKey return the ListNode*,
*other return NULL
*/
static  ListNode* HashTable_getListNode( HashTable_t oHashTable,
                                        const char* pcKey );


/**
*Local help function,Refresh the hash table.
*For the rehash function, this is the array of bucket counts:  
*1024, 2048, 666666. Initially, 
*the bucket count should be set to the first size (1024). When
*HashTable_put detects that the total number of bindings exceeds the 
*current bucket count,1024, a rehash function should increase the 
*bucket count to 2048. Then as number of bindings exceeds 2048, the
*bucket count should be 666666. If the binding count goes 
*over 666666, the bucket count should not be increased again. 
*If successful return 1, otherwise return 0
*/
static int Rehash( HashTable_t oHashTable );


/**
* For help Rehash().
*/
static int ListAssociation_put_help_rehash( ListAssociation* pListAssociation,
                                            ListNode* pListNode );


/**
* Update giBucketCountIndex.
* In some cases(example cycle test) the  giBucketCountIndex may be wrong,
* so need update it to the real index.
*/
static int GetBucketSizeIndex( unsigned long uiBucketCounts );

// HashTable_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];// The default Bucket size
        pListAssociation = ( ListAssociation** )\
                           MyAlloc( temp->uiBucketCount * sizeof( ListAssociation* ) );
        if ( NULL == pListAssociation )
        {
            free( temp );
            return NULL;
        }
        temp->bucketTable = pListAssociation;
    }

    return temp;
}


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


    for ( i = 0; i < uiBucketCount; ++i )
    {
        struct ListAssociation* pListAssociation = NULL;
        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 )// Release key
            {
                free ( temp->key );
            }

            free ( temp ); // Release ListNode

            temp =  pListAssociation->head_node;
        }
        free( pListAssociation );// Release ListAssociation
        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 );

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


    // Check if the Bucket counts < 666666
    if ( oHashTable->uiBucketCount < kBucketCount[ARRAY_SIZE - 1] )
    {
        // If bindings counts > Bucket counts,refresh the hash table,
        if ( oHashTable->uiBindingsCount >= oHashTable->uiBucketCount )
        {
            // Refresh the hash table
            if ( 0 == Rehash( oHashTable ) )
            {
                return 0;
            }
        }
    }

    ListAssociation* pListAssociation = HashTable_getListAssociation( oHashTable, pcKey );
    if ( NULL == pListAssociation ) // There is no ListAssociation* in table
    {
        pListAssociation = ( ListAssociation* )MyAlloc ( sizeof( ListAssociation ) );

        if ( NULL == pListAssociation )
        {
            return 0;
        }
        // Put pListAssociation into table
        oHashTable->bucketTable[HashTable_hash( pcKey, oHashTable->uiBucketCount )] = pListAssociation;
    }

    // Insert the bindings into ListAssociation
    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;
    }

    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;
}


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;
    unsigned i = 0;

    // The table is empty, return
    if ( 0 == HashTable_getLength( oHashTable ) )
    {
        return;
    }


    for ( i = 0; i < uiBucketCount; ++i )
    {
        ListAssociation* pListAssociation = oHashTable->bucketTable[i];
        if ( NULL == pListAssociation )
        {
            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;
}



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, const char* pcKey )
{
    assert ( oHashTable );
    assert ( pcKey );

    ListAssociation* pListAssociation = HashTable_getListAssociation( oHashTable, pcKey );

    if ( NULL == pListAssociation )
    {
        return NULL;
    }

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

    /*
    *The key-value maybe has been removed
    *or has not inserted successfully
    */
    if ( NULL == temp )
    {
        return NULL;
    }

    if ( 0 == strcmp ( pcKey, temp->key ) )   // Head node is the node which need to be removed
    {

        value = temp->value;
        pListAssociation->head_node = temp->next;
        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 ( NULL == temp )
        {
            return NULL;
        }
        if ( 0 == strcmp ( pcKey, temp->key ) )   // Find it
        {

            pre_node->next = temp->next;
            value = temp->value;
            // Do not forget to release the memory
            free ( temp->key );
            free ( temp );
            // Update the counts
            pListAssociation->uiNodeLength = pListAssociation->uiNodeLength - 1;
            oHashTable->uiBindingsCount =  oHashTable->uiBindingsCount - 1;
            return value;
        }
    }
    return NULL;

}



static void* MyAlloc ( unsigned long size )
{
    void* tmp;

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

    return tmp;
}


static  ListAssociation* HashTable_getListAssociation( HashTable_t oHashTable,
                                                      const char* pcKey )
{
    assert ( oHashTable );
    assert ( pcKey );

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


    int iHash_key = -1;

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

    return oHashTable->bucketTable[iHash_key];
}



static  ListNode* ListAssociation_getListNode( const ListAssociation* pListAssociation,
                                               const char* pcKey )
{
    assert ( pListAssociation );
    assert ( pcKey );

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


    ListNode* pListNode = pListAssociation->head_node;

    while ( pListNode )
    {
        if ( 0 == strcmp ( pcKey, pListNode->key ) )
        {
            return pListNode;
        }

        pListNode =  pListNode->next;
    }
    return NULL;
}



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 ) );
        if ( NULL == new_node )
        {
            return 0;
        }

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

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

        if ( 0 == pListAssociation->uiNodeLength )   // The list is empty
        {
            pListAssociation->head_node = new_node;
            pListAssociation->tail_node = new_node;
            pListAssociation->uiNodeLength = 1;

            return 1;
        }

        if ( 1 == pListAssociation->uiNodeLength )   // Only one key-value in list
        {
            pListAssociation->head_node->next = new_node;
            pListAssociation->tail_node = new_node;
            pListAssociation->uiNodeLength = 2;

            return 1;
        }
        else     // More than one key-value in list
        {
            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 ( 0 == strlen( pcKey ) )
    {
        return NULL;
    }

    ListAssociation* pListAssociation = NULL;

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

    pListAssociation = HashTable_getListAssociation( oHashTable, pcKey );

    if ( NULL == pListAssociation )
    {
        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 );

    unsigned long uiBucketCounts = 0;

    if ( ( ARRAY_SIZE - 1 ) == giBucketCountIndex )
    {
        return 1;
    }

    ++giBucketCountIndex;
    uiBucketCounts = kBucketCount[giBucketCountIndex];

    // Create a new ListAssociation** base on uiBucketCounts
    struct ListAssociation** pNewListAssociation = NULL;
    pNewListAssociation = ( ListAssociation** )MyAlloc( uiBucketCounts * sizeof( ListAssociation* ) );
    if ( NULL == pNewListAssociation )
    {
        return 0;
    }

    // Update the old ListAssociation** to new ListAssociation**
    {
        unsigned long uiBucketCount = oHashTable->uiBucketCount;
        unsigned long i = 0;

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

            if ( NULL == pOldListAssociation )
            {
                continue;
            }

            ListNode* pTempListNode = pOldListAssociation->head_node;
            // Iterate current ListNode and update the bindings to new table
            while ( pTempListNode )
            {
                int iHash_key = -1;

                iHash_key = HashTable_hash( pTempListNode->key, uiBucketCounts );
                assert( iHash_key >= 0 );

                ListAssociation* pListAssociation = pNewListAssociation[iHash_key];
                if ( NULL == pListAssociation ) // There is no ListAssociation* in table
                {
                    pListAssociation = ( ListAssociation* )MyAlloc ( sizeof( ListAssociation ) );

                    if ( NULL == pListAssociation )
                    {
                        return 0;
                    }
                    // Put ListAssociation* into new table
                    pNewListAssociation[iHash_key] = pListAssociation;
                }

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

                pTempListNode =  pOldListAssociation->head_node;
            }
            // Do not forget to release memory
            free( pOldListAssociation );
            pOldListAssociation = NULL;
        }
    }

    free( oHashTable->bucketTable );

    // Update for the new BucketSize
    oHashTable->bucketTable = pNewListAssociation;
    oHashTable->uiBucketCount = uiBucketCounts;

    return 1;

}


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

    if ( ListAssociation_getListNode( pListAssociation, pListNode->key ) )
    {
        return 0;
    }
    else
    {

        if ( 0 == pListAssociation->uiNodeLength )   // The list is empty
        {
            pListAssociation->head_node = pListNode;
            pListAssociation->tail_node = pListNode;
            pListAssociation->tail_node->next = NULL;
            pListAssociation->uiNodeLength = 1;

            return 1;
        }

        // Only one key-value in list
        if ( 1 == pListAssociation->uiNodeLength )
        {
            pListAssociation->head_node->next = pListNode;
            pListAssociation->tail_node = pListNode;
            pListAssociation->tail_node->next = NULL;
            pListAssociation->uiNodeLength = 2;

            return 1;
        }
        else     // More than one key-value in list
        {
            pListAssociation->tail_node->next = pListNode;
            pListAssociation->tail_node = pListNode;
            pListAssociation->tail_node->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;
}












  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值