哈希表

本文介绍了哈希表的概念,包括数字和字符关键字的哈希值计算方法,如直接定址法、除留余数法和数字分析法。重点讲述了分离链式法解决哈希冲突,并提供了哈希表的创建、查找、插入和销毁的代码示例。此外,还强调了哈希函数设计的目标是计算简单和地址分布均匀。
摘要由CSDN通过智能技术生成

目录

 

1.什么是哈希表

    一.数字关键字哈希值计算方法

     二.字符关键字哈希值计算方法

2.哈希表分离链式法       

3.怎么用哈希表

4.说明


1.什么是哈希表

        哈希表就是散列表,是将输入关键字,通过散列函数计算得到哈希值,用这个哈希值去快速查找或定位的数据结构。

        散列(哈希)函数(hash)就是针对输入的关键字,不管是用于比较的数字还是字符串,设计的一个函数或算法。该函数或算法可以通过一种计算,将输入映射为一个确定的数字,然后将这个数字作为数组的索引,从而在O(1)的时间内快速找到目标。例如,假设输入为ABC,哈希表为静态数组A[100],通过哈希函数hash(ABC) = 8,得到输入ABC的哈希值为8,那么A[8]就是要查找的目标或者存放位置。(存还是查找看情况)

        哈希表好用,但是哈希函数不好构造,主要是因为针对不同的输入,通过一般哈希函数计算出来的哈希值他有可能是一样的,也就是所谓的哈希冲突。一个好的哈希函数应该考虑两个因素:a.计算简单,以便提高转换速度 b.关键词对应的地址空间分布均匀,以减少冲突。程序中设计的关键字无非字符串和数字,针对这两种不同的关键字,有不同的哈希计算策略。

    一.数字关键字哈希值计算方法

  1. 直接定址法:取关键字的某个线性函数值作为散列地址,比如hash(key) = a*key+b,其中a,b是常数。

  2. 除留余数法:hash(key) = key%p。就是关键字除以一个数,用除不尽的余数作为哈希值。但是像2,4这样的数作为除数也有很大问题,8%2 = 0,16%2 = 0,凡是能被2除尽的,余数都为0。因此对除数的选择需要一点策略,书上说一般采用素数。

  3. 数字分析法 :分析数字关键字在各个位上的变化情况,取比较随机的作为散列地址,比如去手机号码最后四位作为散列地址。

     

     二.字符关键字哈希值计算方法

    

既然哈希冲突是个普遍现象,那就坦然接收,想办法解决冲突。书上说有开放地址法,线性探测法,平方探测法,分离链式法等。

2.哈希表分离链式法       

        我比较倾向于使用分离链式法来解决哈希冲突,比较好理解。链式法数据结构定义如下:

typedef int Index;     /* 散列地址类型 */
/******** 单链表的定义 ********/
typedef struct LNode *PtrToLNode;
struct LNode {
    ElementType Data;
    PtrToLNode Next;
};

typedef PtrToLNode List;
/******** 单链表的定义 ********/

typedef struct TblNode *HashTable; /* 散列表类型 */
struct TblNode {   /* 散列表结点定义 */
    int TableSize; /* 表的最大长度 */
    List Heads;    /* 指向链表头结点的数组 */
};

        可以看到,哈希表就是链表数组,数组的每一个元素都是单向链表头指针。我们根据哈希函数计算得到散列值,用这个散列值作为数组的下标来看当前元素是否为空,不为空说明有冲突,就直接追加到链表好了。有了哈希函数和哈希表结构,就可以利用哈希表做插入和查找了。 

3.怎么用哈希表

        常用对哈希表的操作包括,创建哈希表,销毁哈希表,哈希查找,哈希插入表等。用以下代码实例说明。

//创建哈希表
HashTable CreateTable( int TableSize )
{
    HashTable H;
    int i;

    H = (HashTable)malloc(sizeof(struct TblNode));
    /* 保证散列表最大长度是素数,具体见代码5.3 */
    H->TableSize = NextPrime(TableSize);

    /* 以下分配链表头结点数组 */
    H->Heads = (List)malloc(H->TableSize*sizeof(struct LNode));
    /* 初始化表头结点 */
    for( i=0; i<H->TableSize; i++ ) {
         H->Heads[i].Data[0] = '\0';
         H->Heads[i].Next = NULL;
    }

    return H;
}

//哈希查找,这里哈希函数可以用简单的除留余数法来实现
Position Find( HashTable H, ElementType Key )
{
    Position P;
    Index Pos;
    
    Pos = Hash( Key, H->TableSize ); /* 初始散列位置 */
    P = H->Heads[Pos].Next; /* 从该链表的第1个结点开始 */
    /* 当未到表尾,并且Key未找到时 */ 
    while( P && strcmp(P->Data, Key) )
        P = P->Next;

    return P; /* 此时P或者指向找到的结点,或者为NULL */
}

//根据关键字查找
bool Insert( HashTable H, ElementType Key )
{
    Position P, NewCell;
    Index Pos;
    
    P = Find( H, Key );
    if ( !P ) { /* 关键词未找到,可以插入 */
        NewCell = (Position)malloc(sizeof(struct LNode));
        strcpy(NewCell->Data, Key);
        Pos = Hash( Key, H->TableSize ); /* 初始散列位置 */
        /* 将NewCell插入为H->Heads[Pos]链表的第1个结点 */
        NewCell->Next = H->Heads[Pos].Next;
        H->Heads[Pos].Next = NewCell; 
        return true;
    }
    else { /* 关键词已存在 */
        printf("键值已存在");
        return false;
    }
}

//销毁哈希表
void DestroyTable( HashTable H )
{
    int i;
    Position P, Tmp;
    
    /* 释放每个链表的结点 */
    for( i=0; i<H->TableSize; i++ ) {
        P = H->Heads[i].Next;
        while( P ) {
            Tmp = P->Next;
            free( P );
            P = Tmp;
        }
    }
    free( H->Heads ); /* 释放头结点数组 */
    free( H );        /* 释放散列表结点 */
}

4.说明

        文档全部截图和代码均来自中国大学mooc中,浙江大学数据和算法课程。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值