C语言数据结构哈希表详解

yizhihongxing

C语言数据结构哈希表详解

什么是哈希表?

哈希表(Hash Table)是一种采用散列函数(hash函数)将数据映射到一个固定长度的数组中,并且以 O(1) 的时间复杂度进行数据插入、查找、删除操作的数据结构。哈希表主要由以下三个组成部分构成:
- 数组:用于存储映射到对应下标上的数据。
- 散列函数:将数据映射到数组下标上的规则。
- 冲突处理方式:当不同的数据映射到同一下标时,需要采取的处理措施,如开放地址法、链地址法等。

实现哈希表的关键点

哈希表的实现过程中,有以下几个关键点需要掌握:

哈希函数的设计

哈希函数的设计决定了哈希表的效率和数据存储的均衡性,通常需要符合以下要求:
- 易于计算:计算哈希值的时间复杂度要尽量低。
- 均匀分布:将数据散列到数组中的下标需要尽量均匀,减少冲突的发生。
- 低冲突率:哈希算法计算出相同哈希值的输入应尽量少。

哈希表的扩容和缩容

当哈希表存储的数据量达到一定阈值时,需要对哈希表进行扩容,以保证哈希表的效率和空间利用率。同时,当哈希表中存储的数据量变小时,还需要对哈希表进行缩容以减少空间的浪费。

冲突的解决方式

当不同的数据映射到同一下标时,可以采取以下几种冲突解决方式:
- 开放地址法:尝试向其他的空闲位置插入元素,类似于寻找新的住址。
- 链地址法:将多个具有同一哈希值的元素存储在同一个链表中。

哈希表的实现示例

以下是一个使用链地址法实现哈希表的示例,详见代码:

#include <stdlib.h>
#include <string.h>

// 哈希表节点结构体
typedef struct HashNode {
    char* key;
    char* value;
    struct HashNode* next;
} HashNode;

// 哈希表结构体
typedef struct HashTable {
    HashNode** nodes;
    int size;
} HashTable;

// 创建哈希表
HashTable* createHashTable(int size) {
    HashTable* hashTable = (HashTable*)malloc(sizeof(HashTable));
    hashTable->nodes = (HashNode**)calloc(size, sizeof(HashNode*));
    hashTable->size = size;
    return hashTable;
}

// 计算哈希值
int hash(char* key, int size) {
    unsigned int hashValue = 0;
    for (int i = 0; i < strlen(key); i++) {
        hashValue = hashValue * 31 + key[i];
    }
    return hashValue % size;
}

// 插入键值对
void put(HashTable* hashTable, char* key, char* value) {
    int index = hash(key, hashTable->size);
    HashNode* node = hashTable->nodes[index];
    while (node != NULL) {
        if (strcmp(node->key, key) == 0) {
            // 如果键已经存在,更新其对应的值
            strcpy(node->value, value);
            return;
        }
        node = node->next;
    }
    // 如果键不存在,创建新的节点并插入链表头部
    HashNode* newNode = (HashNode*)malloc(sizeof(HashNode));
    newNode->key = (char*)malloc(strlen(key) + 1);
    strcpy(newNode->key, key);
    newNode->value = (char*)malloc(strlen(value) + 1);
    strcpy(newNode->value, value);
    newNode->next = hashTable->nodes[index];
    hashTable->nodes[index] = newNode;
}

// 获取键对应的值
char* get(HashTable* hashTable, char* key) {
    int index = hash(key, hashTable->size);
    HashNode* node = hashTable->nodes[index];
    while (node != NULL) {
        if (strcmp(node->key, key) == 0) {
            return node->value;
        }
        node = node->next;
    }
    return NULL;
}

// 删除键值对
void removeByKey(HashTable* hashTable, char* key) {
    int index = hash(key, hashTable->size);
    HashNode* node = hashTable->nodes[index];
    HashNode* preNode = NULL;
    while (node != NULL) {
        if (strcmp(node->key, key) == 0) {
            if (preNode == NULL) {
                // 如果删除的节点是链表的第一个节点,需要将链表头部指向第二个节点
                hashTable->nodes[index] = node->next;
            } else {
                // 如果删除的节点不是链表的第一个节点,需要将前一个节点指向下一个节点
                preNode->next = node->next;
            }
            free(node->key);
            free(node->value);
            free(node);
            return;
        }
        preNode = node;
        node = node->next;
    }
}

// 删除哈希表
void deleteHashTable(HashTable* hashTable) {
    for (int i = 0; i < hashTable->size; i++) {
        HashNode* node = hashTable->nodes[i];
        while (node != NULL) {
            HashNode* nextNode = node->next;
            free(node->key);
            free(node->value);
            free(node);
            node = nextNode;
        }
    }
    free(hashTable->nodes);
    free(hashTable);
}

// 示例:使用哈希表存储学生信息
int main() {
    HashTable* studentTable = createHashTable(100);
    put(studentTable, "Tom", "20");
    put(studentTable, "Jerry", "19");
    put(studentTable, "Alice", "21");
    put(studentTable, "Bob", "18");

    printf("Bob's age is %s\n", get(studentTable, "Bob"));

    removeByKey(studentTable, "Alice");

    printf("Alice's age is %s\n", get(studentTable, "Alice"));

    deleteHashTable(studentTable);
    return 0;
}

在上述代码中,我们使用了链地址法解决了哈希冲突,并实现了插入、查找、删除等操作。此外,我们还根据实际需求,将该哈希表用于存储学生信息,以便更好地说明哈希表的应用场景。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C语言数据结构哈希表详解 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月16日

相关文章

  • C++高级数据结构之二叉查找树

    C++高级数据结构之二叉查找树 什么是二叉查找树 二叉查找树,也称二叉搜索树(BST,Binary Search Tree),是一种常见的基于二叉树的数据结构,主要用于快速查找与排序。在二叉查找树上,左子树的每个节点都比其根节点小,右子树的每个节点都比其根节点大,同时整棵树也满足二叉树的性质。 二叉查找树的实现 我们可以通过C++语言实现二叉查找树的基本操作…

    数据结构 2023年5月17日
    00
  • 字符串算法–$\mathcal{KMP,Trie}$树

    \(\mathcal{KMP算法}\) 实际上,完全没必要从\(S\)的每一个字符开始,暴力穷举每一种情况,\(Knuth、Morris\)和\(Pratt\)对该算法进行了改进,称为 \(KMP\) 算法。 而\(KMP\)的精髓在于,对于每次失配之后,我都不会从头重新开始枚举,而是根据我已经得知的数据,从某个特定的位置开始匹配;而对于模式串的每一位,都有…

    算法与数据结构 2023年4月18日
    00
  • Java数据结构之双向链表的实现

    Java数据结构之双向链表的实现 一、双向链表的定义 双向链表是一种包含两个指针的链表数据结构,每个节点都有两个指针,一个指向前一个节点,一个指向后一个节点。 二、双向链表的实现 1. 定义节点 首先,我们需要定义一个节点类,包含节点的值,指向前一个节点的指针pre和指向后一个节点的指针next,代码如下: public class Node { int v…

    数据结构 2023年5月17日
    00
  • C数据结构之双链表详细示例分析

    作为本网站的作者,我很高兴为你讲解C数据结构之双链表详细示例分析的完整攻略。 双链表简介与定义 双链表是链表的一种,在链表中每一个节点都有一个指针域,指向下一个节点,这个指针域称为next指针;而在双链表中每一个节点也有两个指针域,一个指向前驱节点,另一个指向后继节点,即prev指针与next指针。由于双链表存在两个指针域,因此它支持双向遍历,无论是正向还是…

    数据结构 2023年5月17日
    00
  • c语言数据结构之并查集 总结

    C语言数据结构之并查集总结 简介 并查集,也称作不相交集合,是一种树型的数据结构。并查集用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。 并查集只有两个操作: find:确定某个元素属于哪个子集。它可以被用来确定两个元素是否属于同一子集。 union:将两个子集合并成同一个集合。 基本实现 以快速查找find和…

    数据结构 2023年5月17日
    00
  • php数据结构与算法(PHP描述) 查找与二分法查找

    以下是详细讲解“php数据结构与算法(PHP描述) 查找与二分法查找”的完整攻略。 1. 数据结构与算法简介 数据结构是计算机中存储和组织数据的方式。它涉及到数据的表示、处理和存储方式等。 算法则是完成特定任务的步骤集合。算法设计可以优化计算机程序的效率和速度。 PHP是一种非常流行的服务器端脚本语言,数据结构和算法对web开发者来说非常重要。因此,我们需要…

    数据结构 2023年5月17日
    00
  • C++抽象数据类型介绍

    C++抽象数据类型介绍 什么是抽象数据类型? 抽象数据类型(Abstract Data Type,ADT),是数据类型的一个数学模型。它实现了数据类型的抽象过程,将数据与操作分离,使得操作具有独立性,而数据只作为函数参数和返回值存在。 举个例子,ADT可以定义一个栈(Stack),栈的实现需要以下操作: 初始化栈 压入数据 弹出数据 获取栈顶数据 检查栈是否…

    数据结构 2023年5月17日
    00
  • Java数据结构之线段树的原理与实现

    Java数据结构之线段树的原理与实现 什么是线段树 线段树是一种基于分治思想的数据结构,它可以用来解决各种区间查询问题,例如区间求和、最大值、最小值等等。在算法竞赛和数据结构课程中,线段树被广泛应用,是一种非常实用的数据结构。 线段树的基本原理 线段树是一种二叉树,它的每个节点包含一个区间,叶子节点表示区间中的单个元素,非叶子节点表示区间的合并。 线段树的建…

    数据结构 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部