哈希表(拉链法)

> 开散列法又叫链地址法(开链法)。

开散列法:首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个
桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。
设元素的关键码为37, 25, 14, 36, 49, 68, 57, 11, 散列表为HT[12],表的大小为12,散列函数为Hash(x) = x % 11
Hash(37)=4
Hash(25)=3
Hash(36)=3
Hash(49)=5
Hash(68)=2
Hash(11)=0
使用哈希函数计算出每个元素所在的桶号,同一个桶的链表中存放哈希冲突的元素。
通常,每个桶对应的链表结点都很少,将n个关键码通过某一个散列函数,存放到散列表中的m个桶中,那么每一个桶中链表的平均 长度为 。以搜索平均长度为 的链表代替了搜索长度为 n 的顺序表,搜索效率快的多。
应用链地址法处理溢出,需要增设链接指针,似乎增加了存储开销。事实上: 由于开地址法必须保持大量的空闲空间以确保搜索效率,如二次探查法要求装载因子a <= 0.7,而表项所占空间又比指针大的多, 所以使用链地址法反而比开地址法节省存储空间。

开散列的实现(拉链法)
HashNode.h

#pragma once
#include <stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int KeyType;
typedef int valueType;
typedef struct HashNode
{
    KeyType _key;
    valueType _value;
    struct HashNode*_next;
}HashNode;
typedef struct HashTable
{
    HashNode** table;
    size_t _size;
    size_t _N;
}HashTable;
HashNode*BuyHashNode(KeyType key, valueType value);
size_t GetHashTablePrime(size_t N);
void HashTableInit(HashTable*ht,int size);//初始化
void HashTablePrint(HashTable*ht);
int HashTableInsert(HashTable*ht, KeyType key, valueTypevalue);//
HashNode* HashTableFind(HashTable*ht, KeyType key);//查找
size_t HashTableErase(HashTable*ht, KeyType key);//删除
void HashTableDesTroy(HashTable*ht);//销毁

HashNode.c

#include"HashNode.h"
HashNode* BuyHashNode(KeyType key, valueType value)
{
    HashNode*Node = (HashNode*)malloc(sizeof(HashNode));
    assert(Node);
    Node->_key = key;
    Node->_value = value;
    Node->_next = NULL;
    return Node;
}
void HashTableInit(HashTable*ht,int size)//初始化
{
    assert(ht);
    ht->_size = 0;
    ht->_N = size;
    //这里要注意开辟空间是给指针数组
    ht->table = (HashTable**)malloc(sizeof(HashTable*)*ht->_N);
    assert(ht->table);
    memset(ht->table, 0, sizeof(HashTable*)*ht->_N);
}
size_t GetHashTablePrime(size_t N)
{
    static const unsigned long _PrimeList[28] =
        {
            53ul, 97ul, 193ul, 389ul, 769ul,
            1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
            49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
            1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
            50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
            1610612741ul, 3221225473ul, 4294967291ul
        };
        for (int i = 0; i < 28; i++)
        {
            if (N < _PrimeList[i])
            {
                N = _PrimeList[i];
                return N;
            }

        }
        return _PrimeList[27];


}
size_t HashFunc(KeyType key, size_t N)
{
    return key%N;
}

int HashTableInsert(HashTable*ht, KeyType key, valueType value)//插入
{

    assert(ht);
    HashNode* node = BuyHashNode(key, value);
    if (ht->_size * 10 / ht->_N > 7)
    {
        //扩容
        size_t newN = GetHashTablePrime(ht->_N);
        HashTable newht;
        HashTableInit(&newht, newN);

        for (size_t i = 0; i < ht->_N; i++)
        {
            HashNode* cur = ht->table[i];//得到链表表头节点的地址
            if (ht->table[i])
            {
                size_t index = HashFunc(cur->_key, newht._N);//重新计算原表中的值在新表中的位置
                while (cur)
                {
                    cur->_next = newht.table[index];//将原表头节点的下一个指向新表头结点
                    newht.table[index] = cur;//原表头结点赋给新表头结点实现原表值映射到新表中
                    cur = cur->_next;
                }

            }
        }
        free(ht->table);
        ht->_N = newN;//将新表中的扩容给旧表
        ht->table = newht.table;//新表赋给原表
    }

    size_t index = HashFunc(key, ht->_N);
    if (ht->table[index])
    {
        HashNode*cur = ht->table[index];
        while (cur)
        {
            if (cur->_key == key)
            {
                return -1;
            }
            cur = cur->_next;
        }

    }
    node->_next = ht->table[index];
    ht->table[index] = node;
    ht->_size++;
    return 0;
}
void HashTablePrint(HashTable*ht)
{
    for (size_t i = 0; i < ht->_N; i++)
    {
        if (ht->table[i])
        {
            HashNode*cur = ht->table[i];
            while (cur)
            {
                printf("[%d]->%d\n ",i, cur->_key);
                cur = cur->_next;
            }

        }
    }
}
HashNode *HashTableFind(HashTable*ht, KeyType key)
{
    assert(ht);
    size_t index = HashFunc(key, ht->_N);
    if (ht->table[index])
    {
        if (ht->table[index]->_key=key)
        {
            return ht->table[index];
        }
        else
        {
            HashNode*cur = ht->table[index];
                while (cur)
                {
                    if (cur->_key==key)
                    {
                        return cur;
                    }
                    cur = cur->_next;
                }
                return NULL;
        }
    }
}
size_t HashTableErase(HashTable*ht, KeyType key)
{
    assert(ht);
    size_t index = HashFunc(key, ht->_N);
    if (ht->table[index])
    {
        HashNode* cur = ht->table[index];
        HashNode* prev = ht->table[index];
        while (cur)
        {
            if (cur == prev&&cur->_key == key)//只有一个节点
            {
                ht->table[index] = cur->_next;
                free(cur);
                cur = NULL;
                ht->_size--;
                return 0;
            }
            else if (cur->_key == key)//多个节点
            {
                prev = cur->_next;
                free(cur);
                cur = NULL;
                ht->_size--;
                return 0;
            }
            prev = cur;
            cur = cur->_next;
        }
        return -1;
    }

    else
    {
        return -1;
    }
}
void HashTableDesTroy(HashTable*ht)
{
    assert(ht);
    for (size_t i = 0; i < ht->_N; i++)
    {
        if (ht->table[i])
        {
            HashNode*cur = ht->table[i];
            while (cur)
            {
                HashNode*tmp = cur;
                cur = cur->_next;
                free(tmp);
                tmp = NULL;

            }
        }
    }
    free(ht->table);
    ht->table = NULL;
    ht->_N = 0;
    ht->_size = 0;
}

test.c

#include"HashNode.h"
void test()
{
    HashTable ht;
    HashTableInit(&ht,5);
    HashTableInsert(&ht, 8,0);
    HashTableInsert(&ht, 11, 0);
    HashTableInsert(&ht, 10, 0);
    HashTableInsert(&ht, 12, 0);
    HashTableInsert(&ht, 2, 0);
    HashTableInsert(&ht, 5, 0);
    HashTableInsert(&ht, 20, 0);
    HashTablePrint(&ht);
    printf("\n");

    printf("%d\n ", HashTableFind(&ht, 10)->_key);
    printf("%d\n ", HashTableFind(&ht, 12)->_key);
    printf("%d\n ",HashTableFind(&ht, 20)->_key);
    printf("%d\n", HashTableFind(&ht, 8)->_key);
    printf("%p\n ", HashTableFind(&ht, 1)->_key);
    printf("\n");
    HashTableErase(&ht, 8);
    HashTableErase(&ht, 2);
    HashTableErase(&ht, 5);
    HashTableErase(&ht, 20);
    HashTablePrint(&ht);
    printf("\n");
    HashTableDesTroy(&ht);
    HashTablePrint(&ht);


}
int main()
{
    test();
    system("pause");
    return 0;
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值