哈希表的基本操作

哈希表

若关键字为k,则其值存放在f(k)的存储位置上。由此,不需比较便可直接取得所查记录。称这个对应关系f()为散列函数,按这个思想建立的表为散列表(即哈希表).

对不同的关键字可能得到同一散列地址,即k1≠k2,而f(k1)=f(k2)
这种现象称为碰撞

散列函数能使对一个数据序列的访问过程更加迅速有效,通过散列函数,数据元素将被更快地定位.

散列函数有很多种
常用的有:

  • 直接寻址法:取关键字或关键字的某个线性函数值为散列地址。即H(key)=key或H(key) = a·key + b,其中a和b为常数(这种散列函数叫做自身函数)。若其中H(key)中已经有值了,就往下一个找,直到H(key)中没有值了,就放进去。
  • 平方取中法:当无法确定关键字中哪几位分布较均匀时,可以先求出关键字的平方值,然后按需要取平方值的中间几位作为哈希地址。这是因为:平方后中间几位和关键字中每一位都相关,故不同关键字会以较高的概率产生不同的哈希地址。
  • 除留余数法:取关键字被某个不大于散列表表长m的数p除后所得的余数为散列地址。即 H(key) = key MOD p,p<=m。不仅可以对关键字直接取模,也可在折叠、平方取中等运算之后取模。对p的选择很重要,一般取素数或m,若p选的不好,容易产生同义词。

同样处理碰撞问题的方法也有很多种
常用的有:

  • 线性探测: 如果k1≠k2,而f(k1)=f(k2), 就继续往后寻找, 找到一个空的位置, 然后放进去.
  • 链地址法: 这种方法数组的每个元素是一个链表的头指针, 如果k1≠k2,而f(k1)=f(k2), 就往链表中头插节点.

实现代码

线性探测
/*================================================================

# File Name: hash.h
# Author: rjm
# mail: [email protected]
# Created Time: 2018年05月18日 星期五 18时26分13秒

================================================================*/


// 哈希表
#pragma once

#define TEST_HEAD printf("\n==================%s===============\n", __FUNCTION__)
#define HashMaxSize 1000

typedef int KeyType; // 要插入的值
typedef int ValueType;
// 计算下标的函数
typedef int (*HashFunc)(KeyType key);

typedef enum Stat {
    Empty, // 空状态, 表示这个位置还没有元素插入
    valid, // 有效状态, 表示这个位置已经有元素插入了
    deleted, // 已删除状态, 表示这个位置的元素已经被删除了
} Stat;

// 键值对
typedef struct HashElem {
    KeyType key;
    ValueType value;
    Stat stat; //每个位置的状态
} HashElem;

int func(KeyType key)
{
    return key % HashMaxSize;
}

typedef struct HashTable {
    HashElem data[HashMaxSize]; //哈希表数组, 存的是包含一组键值对的结构体
    size_t size; //哈希表当前有效元素的个数
    HashFunc func; //计算下标的函数
} HashTable;


// 初始化
void HashInit(HashTable* ht, HashFunc hash_func);

// 插入元素
void HashInsert(HashTable* ht, KeyType key, ValueType value);

// 查找
int HashFind(HashTable* ht, KeyType key, int* to_find);

// 删除
void HashRemove(HashTable* ht, KeyType key);

// 销毁
void HashDestroy(HashTable* ht);
/*================================================================

# File Name: hash.c
# Author: rjm
# mail: [email protected]
# Created Time: 2018年05月18日 星期五 18时25分38秒

================================================================*/

// 哈希表
#include <stdio.h>
#include "hash.h"

// 初始化
void HashInit(HashTable* ht, HashFunc hash_func)
{
    ht->size = 0;
    ht->func = hash_func;
    for(int i=0; i<HashMaxSize; i++)
    {
        ht->data[i].stat = Empty;
    }
    return ;
}
// 插入
void HashInsert(HashTable* ht, KeyType key, ValueType value)
{
    if(ht->size == HashMaxSize)
    {
        // 哈希表已经满了
        return ;
    }
    int offset = func(key);
    ValueType val;
    if(HashFind(ht, key, &val) == 1)
      return ;
    if(ht->data[offset].stat != valid)
    {
        ht->data[offset].key = key;
        ht->data[offset].value = value;
        ht->data[offset].stat = valid;
        ++ht->size;
    }
    else
    {
        while(1)
        {
            offset++;
            if(offset == HashMaxSize)
            {
                offset = 0;
            }
            if(ht->data[offset].stat != valid)
              break;
        }
        ht->data[offset].key = key;
        ht->data[offset].value = value;
        ht->data[offset].stat = valid;
        ++ht->size;
    }
}
// 查找
int HashFind(HashTable* ht, KeyType key, int* to_find)
{
    if(ht->size == 0)
    {
        // 哈希表为空
        return 0;
    }
    int offset = func(key);
    if(ht->data[offset].key == key)
    {
        *to_find = offset;
        return 1;
    }
    else
    {
        while(ht->data[offset].stat != Empty)
        {
            ++offset;
            if(offset == HashMaxSize)
            {
                offset = 0;
            }
            if(ht->data[offset].key == key && ht->data[offset].stat == valid)
            {
                *to_find = offset;
                return 1;
            }
        }
        return 0;
    }
}
// 删除
void HashRemove(HashTable* ht, KeyType key)
{
    if(ht->size == 0)
    {
        // 哈希表为空
        return ;
    }
    int offset = 0;
    int ret = HashFind(ht, key, &offset);
    if(ret == 0)
    {
        // 元素不存在
        return ;
    }
    // 如果元素存在, 则把它的状态改为已删除
    ht->data[offset].stat = deleted;
    --ht->size;
}
// 销毁
void HashDestroy(HashTable* ht)
{
    ht->size = 0;
    ht->func = NULL;
    for(int i = 0; i<HashMaxSize; i++)
    {
        ht->data[i].stat = Empty;
    }
}

////////////////////////////////////
// 测试函数
////////////////////////////////////

void HashPrint(HashTable* ht, char* msg)
{
    printf("[ %s ]\n", msg);
    for(int i=0; i<HashMaxSize; i++)
    {
        if(ht->data[i].stat == valid)
        {
            printf("[%d| %d:%d]\n", i, ht->data[i].key, ht->data[i].value);
        }
    }
    printf("\n");
}

void TestInit()
{
    TEST_HEAD;
    HashTable ht;
    HashInit(&ht, func);
    HashPrint(&ht, "初始化哈希表");
}
void TestInsert()
{
    TEST_HEAD;
    HashTable ht;
    HashInit(&ht, func);
    HashInsert(&ht, 1, 10);
    HashInsert(&ht, 1001, 12);
    HashInsert(&ht, 2, 10);
    HashInsert(&ht, 2, 15);
    HashInsert(&ht, 1002, 10);
    HashPrint(&ht, "插入 5 个元素, 其中 2 插入两次");
}
void TestFind()
{
    TEST_HEAD;
    HashTable ht;
    HashInit(&ht, func);
    HashInsert(&ht, 1, 10);
    HashInsert(&ht, 1001, 12);
    HashInsert(&ht, 2, 10);
    HashInsert(&ht, 2, 15);
    HashInsert(&ht, 1002, 10);

    int offset = 0;
    int ret = 0;

    HashPrint(&ht, "查找元素 1001");
    ret = HashFind(&ht, 1001, &offset);
    printf("ret expect 1, actual %d\n", ret);
    printf("offset expect 2, actual %d\n", offset);
    printf("\n");
    HashPrint(&ht, "查找元素 1003");
    ret = HashFind(&ht, 1003, &offset);
    printf("ret expect 0, actual %d\n", ret);
}
void TestRemove()
{
    TEST_HEAD;
    HashTable ht;
    HashInit(&ht, func);
    HashInsert(&ht, 1, 10);
    HashInsert(&ht, 1001, 12);
    HashInsert(&ht, 2, 10);
    HashInsert(&ht, 2, 15);
    HashInsert(&ht, 1002, 10);

    HashRemove(&ht, 1001);
    HashPrint(&ht, "删除一个元素 1001");
    HashRemove(&ht, 1002);
    HashPrint(&ht, "再删除一个元素 1002");
    HashRemove(&ht, 2);
    HashPrint(&ht, "再删除一个元素 2");
    HashRemove(&ht, 1);
    HashPrint(&ht, "再删除一个元素 1");
    HashRemove(&ht, 2);
    HashPrint(&ht, "再删除一个元素 2");
    HashRemove(&ht, 2);
    HashPrint(&ht, "对空表删除");
    printf("size = %d\n", ht.size);
}
void TestDestroy()
{
    TEST_HEAD;
    HashTable ht;
    HashInit(&ht, func);
    HashInsert(&ht, 1, 10);
    HashInsert(&ht, 1001, 12);
    HashInsert(&ht, 2, 10);
    HashInsert(&ht, 2, 15);
    HashInsert(&ht, 1002, 10);

    HashDestroy(&ht);
    HashPrint(&ht, "销毁哈希表");
}

int main()
{
    TestInit();
    TestInsert();
    TestFind();
    TestRemove();
    TestDestroy();

    printf("\n");
    printf("\n");
    printf("\n");
    return 0;
}

这里写图片描述
这里写图片描述


链地址法
/*================================================================

# File Name: hash.h
# Author: rjm
# mail: [email protected]
# Created Time: 2018年05月20日 星期日 13时06分57秒

================================================================*/

// 哈希桶
// 数组的每个元素都是一张链表的头指针
#pragma once

#include <stdio.h>

#define TEST_HEAD printf("\n=================%s================\n", __FUNCTION__)
#define BucketMaxSize 1000 // 桶的个数为1000

typedef int KeyType;
typedef int ValType;
typedef int (*HashFunc)(KeyType key);

typedef struct HashElem {
    KeyType key;
    ValType value;
    struct HashElem* next;
} HashElem;

typedef struct HashBucket {
    HashElem* data[BucketMaxSize];
    size_t size; // 当前元素的个数
    HashFunc func;
} HashBucket;

// 初始化
void HashInit(HashBucket* hb, HashFunc func);

// 插入元素
void HashInsert(HashBucket* hb, KeyType key, ValType value);

// 查找元素
HashElem* HashFind(HashBucket* hb, KeyType key, ValType* value);

// 删除元素
void HashRemove(HashBucket* hb, KeyType key);

// 销毁
void HashDestroy(HashBucket* hb);
/*================================================================

# File Name: hash.c
# Author: rjm
# mail: [email protected]
# Created Time: 2018年05月20日 星期日 13时06分36秒

================================================================*/

#include <stdlib.h>
#include "hash.h"

int func(KeyType key)
{
    return key % BucketMaxSize;
}

// 初始化
void HashInit(HashBucket* hb, HashFunc func)
{
    if(hb == NULL)
      return ;
    for(int i=0; i<BucketMaxSize; i++)
    {
        hb->data[i] = NULL;
    }
    hb->size = 0;
    hb->func = func;
}
// 创建新节点
HashElem* CreateNewNode(KeyType key, ValType value)
{
    HashElem* he = (HashElem*)malloc(sizeof(HashElem));
    he->key = key;
    he->value = value;
    he->next = NULL;
    return he;
}
// 插入元素
void HashInsert(HashBucket* hb, KeyType key, ValType value)
{
    if(hb == NULL)
      return ;
    // 根据key算出下标
    int offset = func(key);
    // 创建一个节点保存key和value
    HashElem* new_node = CreateNewNode(key, value);
    // 如果这个下标所保存链表的头指针为 NULL
    // 就直接让头指针指向新节点
    if(hb->data[offset] == NULL)
    {
        hb->data[offset] = new_node;
    }
    // 如果这个下标所保存链表的头指针已经指向了一个节点
    // 就把新节点头插到这个链表中
    else
    {
        if(key == hb->data[offset]->key)
        {
            // 元素相同, 不能插入
            return ;
        }
        else
        {
            // 直接头插
            HashElem* pre = hb->data[offset];
            hb->data[offset] = new_node;
            new_node->next = pre;
        }
    }
    ++hb->size;
}
// 查找元素
HashElem* HashFind(HashBucket* hb, KeyType key, ValType* value)
{
    if(hb == NULL)
      return NULL;
    if(hb->size == 0)
      return NULL;
    int offset = func(key);
    while(hb->data[offset] != NULL)
    {
        if(hb->data[offset]->key == key)
        {
            *value = hb->data[offset]->value;
            return hb->data[offset];
        }
        hb->data[offset] = hb->data[offset]->next;
    }
    return NULL;
}
// 删除元素
void HashRemove(HashBucket* hb, KeyType key)
{
    if(hb == NULL)
      return ;
    if(hb->size == 0)
      return ;
    int offset = func(key);
    HashElem* cur = hb->data[offset];
    HashElem* pre = hb->data[offset];
    if(cur == NULL)
      // 元素不存在
      return ;
    if(cur->next == NULL)
    {
        // 该位置的链表只有一个节点
        free(hb->data[offset]);
        hb->data[offset] = NULL;
        --hb->size;
        return ;
    }
    while(cur->next != NULL)
    {
        if(cur->key == key)
        {
            pre = cur;
        }
        cur = cur->next;
    }
    pre->next = cur->next;
    free(cur);
    cur = NULL;
    --hb->size;
}
// 销毁
void HashDestroy(HashBucket* hb)
{
    if(hb == NULL)
      return ;
    for(int i=0; i<BucketMaxSize; i++)
    {
        hb->data[i] = NULL;
    }
    hb->size = 0;
    hb->func = NULL;
}


///////////////////////////////////////////
// 测试函数
///////////////////////////////////////////
void HashPrint(HashBucket* hb, char* msg)
{
    printf("[ %s ]\n", msg);
    if(hb == NULL)
      return ;
    if(hb->size == 0)
      return ;
    for(int i=0; i<BucketMaxSize; i++)
    {
        HashElem* he = hb->data[i];
        if(hb->data[i] != NULL)
        {
            printf("[%d]\n", i);
            while(hb->data[i] != NULL)
            {
                printf("---[%d:%d]\n", hb->data[i]->key, hb->data[i]->value);
                hb->data[i] = hb->data[i]->next;
            }
        }
        hb->data[i] = he;
    }
}

void TestInit()
{
    TEST_HEAD;
    HashBucket hb;
    HashInit(&hb, func);
    HashPrint(&hb, "初始化哈希桶");
}
void TestInsert()
{
    TEST_HEAD;
    HashBucket hb;
    HashInit(&hb, func);
    HashInsert(&hb, 1001, 18);
    HashInsert(&hb, 1007, 13);
    HashInsert(&hb, 7, 11);
    HashInsert(&hb, 101, 12);
    HashPrint(&hb, "插入 4 个元素");
    HashInsert(&hb, 101, 12);
    HashPrint(&hb, "再插入一个已有的元素");
}
void TestFind()
{
    TEST_HEAD;
    HashBucket hb;
    HashInit(&hb, func);
    HashInsert(&hb, 1001, 18);
    HashInsert(&hb, 1007, 13);
    HashInsert(&hb, 7, 11);
    HashInsert(&hb, 101, 12);

    ValType value;
    HashElem* ret = HashFind(&hb, 1007, &value);
    printf("[查找元素 1007]\n");
    printf("ret excepted 1007, actual %d\n", ret->key);
    printf("value excepted 13, actual %d\n", value);

    ret = HashFind(&hb, 1008, &value);
    printf("[查找不存在的元素 1008]\n");
    printf("ret excepted NULL, actual %p\n", ret);
}
void TestRemove()
{
    TEST_HEAD;
    HashBucket hb;
    HashInit(&hb, func);
    HashInsert(&hb, 1001, 18);
    HashInsert(&hb, 1007, 13);
    HashInsert(&hb, 7, 11);
    HashInsert(&hb, 101, 12);
    HashPrint(&hb, "插入 4 个元素");
    HashRemove(&hb, 1007);
    HashPrint(&hb, "删除一个元素 1007");
    HashRemove(&hb, 1001);
    HashRemove(&hb, 7);
    HashPrint(&hb, "再删除2个元素 1001, 7");
    HashRemove(&hb, 101);
    HashPrint(&hb, "再删除1个元素 101");
    HashRemove(&hb, 101);
    HashPrint(&hb, "对空表进行删除");
}
void TestDestroy()
{
    TEST_HEAD;
    HashBucket hb;
    HashInit(&hb, func);
    HashInsert(&hb, 1001, 18);
    HashInsert(&hb, 1007, 13);
    HashInsert(&hb, 7, 11);
    HashInsert(&hb, 101, 12);
    HashPrint(&hb, "插入 4 个元素");

    HashDestroy(&hb);
    HashPrint(&hb, "销毁哈希表");
}

int main()
{
    TestInit();
    TestInsert();
    TestFind();
    TestRemove();
    TestDestroy();

    printf("\n");
    printf("\n");
    printf("\n");
    return 0;
}

这里写图片描述
这里写图片描述
这里写图片描述

猜你喜欢

转载自blog.csdn.net/sinat_36629696/article/details/80385619