学习《算法导论》第十一章 散列表 总结二

学习《算法导论》第十一章 散列表 总结二

本节学习散列表的两种实现方法:链接法和开放寻址法的代码实现.

链接法

hashsep.h

#ifndef _HashSep_H

typedef int ElementType;

typedef unsigned int Index;

struct ListNode;
typedef struct ListNode* Position;

struct HashTbl;
typedef struct HashTbl* HashTable;

Index Hash(int Key, int TableSize);

HashTable InitializeTable(int TableSize);

void DestroyTable(HashTable H);

Position Find(ElementType Key, HashTable H);

void Insert(ElementType Key, HashTable H);

ElementType Retrieve(Position P);


#endif

hashsep.c

#include "hashsep.h"
#include <stdio.h>
#include <stdlib.h>

#define MinTableSize (10)

typedef Position List;

// 链表结点结构
struct ListNode
{
    ElementType Element;
    Position Next;
};

// 散列表结构
// 这里用的是链接法解决碰撞,所以散列表里存的是指针:指向链表 
// 注意数据结构的定义
struct HashTbl
{
    int TableSize; 
    List* TheLists;
};

// 入参:卫星数据的关键字, HASH表的大小
// 出参:映射到HASH表中的槽的位置
// 功能:散列函数,这里用的散列法是除法散列法,求余
Index Hash(int Key, int TableSize)
{
    return Key % TableSize;
}

// 返回大于N的最小素数
static int NexPrime(int N)
{
    int i;

    if (N % 2 == 0)
        N++;

    for ( ; ; N += 2)
    {
        for (i = 3; i * i <= N; i+= 2)
        {
            if (N % i == 0)
                break;
        }
        return N;
    }
}


// 功能:散列表初始化
HashTable InitializeTable(int TableSize)
{
    HashTable H;
    int i;

    // 合法性检查
    if (TableSize < MinTableSize)
    {
        printf("Table size too small");
        return NULL;
    }

    // 分配散列表的空间
    H = malloc(sizeof(struct HashTbl));
    if (NULL == H)
    {
        printf("H is NULL");
        return NULL;
    }

    H->TableSize = NexPrime(TableSize);

    // 分配链表的总空间大小
    H->TheLists = malloc(sizeof(List) * H->TableSize);
    if (NULL == H)
    {
        printf("H is NULL");
        return NULL;
    }

    // 初始化散列表中链表头的大小
    for (i = 0; i < H->TableSize; i++)
    {
        H->TheLists[i] = malloc(sizeof(struct ListNode));
        if (NULL == H->TheLists[i])
        {
            printf("H is NULL");
            return NULL;
        }
        else
        {
            H->TheLists[i]->Next = NULL;
        }
    }

    return H;
}

// 查找操作
// 返回关键字Key所在的位置,而不是槽的位置
Position Find(ElementType Key, HashTable H)
{
    Position P;
    List L;

    // 通过散列函数查找到关键字Key所在的槽的位置
    L = H->TheLists[Hash(Key, H->TableSize)];
    P = L->Next;

    // 链表中的查找
    while(P != NULL && P->Element != Key)
    {
        P = P->Next;
    }

    return P;
}

// 插入操作
void Insert(ElementType Key, HashTable H)
{
    Position Pos, NewCell;
    List L;

    // 查找关键字Key是否在散列在散列表中
    Pos = Find(Key, H);
    if (NULL == Pos) // Key不在散列表中
    {
        NewCell = malloc(sizeof(struct ListNode));
        if (NULL == NewCell)
        {
            printf("Distribute failed!");
            return;
        }
        else
        {
            // 确定关键字Key要散列到的槽位
            L = H->TheLists[Hash(Key, H->TableSize)];
            // 插入该链表的最前面
            NewCell->Next = L->Next;
            NewCell->Element = Key;
            L->Next = NewCell;
        }
    }
}

// 释放散列表
void DestroyTable(HashTable H)
{
    int i;

    for (i = 0; i < H->TableSize; i++)
    {
        Position P = H->TheLists[i];
        Position Tmp;

        // 释放链表
        while(P != NULL)
        {
            Tmp = P->Next;
            free(P);
            P = Tmp;
        }
    }

    free(H->TheLists);
    free(H);
}

// 返回存放在位置P处的元素
ElementType Retrieve(Position P)
{
    return P->Element;
}

开放寻址法

hashquad.h

#ifndef _HashSep_H

typedef int ElementType;

typedef unsigned int Index;

struct ListNode;
typedef struct ListNode* Position;

struct HashTbl;
typedef struct HashTbl* HashTable;

Index Hash(int Key, int TableSize);

HashTable InitializeTable(int TableSize);

void DestroyTable(HashTable H);

Position Find(ElementType Key, HashTable H);

void Insert(ElementType Key, HashTable H);

ElementType Retrieve(Position P);

HashTable Rehash(HashTable H);

#endif

hashquad.c

#include <stdlib.h>
#include <stdio.h>
#include "hashquad.h"

#define MinTableSize 10

enum KindOfEntry {Legitimate, Empty, Deleted};

// 解决开放寻址法中删除操作的困难
// 将散列表中的每个槽位都进行标记:legitimate empty deleted
struct HashEntry
{
    ElementType Element;
    enum KindOfEntry Info;
};

typedef struct HashEntry Cell;

// 散列表结构
struct HashTbl
{
    int TableSize;
    Cell* TheCells;
};

// 返回大于N的最小素数
static int NexPrime(int N)
{
    int i;

    if (N % 2 == 0)
        N++;

    for ( ; ; N += 2)
    {
        for (i = 3; i * i <= N; i+= 2)
        {
            if (N % i == 0)
                break;
        }
        return N;
    }
}

// 散列函数
Index Hash(ElementType Key, int TableSize)
{
    return Key % TableSize;
}

// 散列表的初始化
HashTable InitializeTable(int TableSize)
{
    HashTable H;
    int i;

    if (TableSize < MinTableSize)
    {
        printf("Table size too small!");
        return NULL;
    }

    // 分配散列表的空间
    H = malloc(sizeof(struct HashTbl));
    if (NULL == H)
    {
        printf("H is NULL");
        return NULL;
    }

    H->TableSize = NextPrime(TableSize);

    H->TheCells = malloc(sizeof(Cell) * H->TableSize);
    if (NULL == H->TheCells)
    {
        printf("H->TheCells is NULL");
        return NULL;
    }

    // 将散列表中每个槽的状态标记为empty
    for(i = 0; i < H->TableSize; i++)
    {
        H->TheCells[i].Info = Empty;
    }

    return H;
}


// 查找操作 
Position Find( ElementType Key, HashTable H )  
{  
    Position CurrentPos;  
    int CollisionNum;  

    CollisionNum = 0;  
    CurrentPos = Hash( Key, H->TableSize );  
    while ( H->TheCells[ CurrentPos ].Info != Empty &&  
        H->TheCells[ CurrentPos ].Element != Key )  
        /* Probably need strcpy! */  
    {  
        CurrentPos += 2 * ++CollisionNum - 1;   // 若该位置不是Empty且存放的关键字不是key,查找下一个
        if ( CurrentPos >= H->TableSize )  
            CurrentPos -= H->TableSize;  
    }  
    return CurrentPos;  
}  

// 插入操作 
void Insert( ElementType Key, HashTable H )  
{  
    Position Pos;  

    Pos = Find( Key, H );  
    if ( H->TheCells[ Pos ].Info != Legitimate )  // 只要该位置不是Legitimate,就可以插入
    {  
        /* Ok to insert here */  
        H->TheCells[ Pos ].Info = Legitimate;  
        H->TheCells[ Pos ].Element = Key; /* Probably need strcpy! */  
    }  
}  

// 更新 
HashTable Rehash( HashTable H )  
{  
    int i, OldSize;  
    Cell *OldCells;  

    OldCells = H->TheCells;  
    OldSize = H->TableSize;  

    /* Get a new, empty table */  
    H = InitializeTable( 2 * OldSize );  

    /* Scan through old table, reinserting into new */  
    for( i = 0; i < OldSize; i++ )  
        if ( OldCells[ i ].Info == Legitimate )  
            Insert( OldCells[ i ].Element, H );  

    free( OldCells );  

    return H;  
}  


ElementType Retrieve( Position P, HashTable H )  
{  
    return H->TheCells[ P ].Element;  
}  

void  
DestroyTable( HashTable H )  
{  
    free( H->TheCells );  
    free( H );  
}

但是以上两种方法都没实现删除操作,开放寻址法的关键字删除后,要将该槽位标记为deleted.

参考文档

http://blog.csdn.net/shuxiao9058/article/details/7433895

点赞