C语言写一个散列表

C语言写一个散列表的完整攻略

什么是散列表?

散列表是一种数据结构,它将键映射到值。通过使用散列函数,散列表可以快速查找数据。散列表可以用于实现字典、哈希表、集合等数据结构。

散列表的实现

散列表的实现可以分为以下几步:

  1. 定义散列表的结构体以及散列表元素的结构体;
  2. 实现散列函数;
  3. 实现插入元素方法;
  4. 实现查找元素方法;
  5. 实现删除元素方法;
  6. 实现销毁散列表方法。
// 定义散列表元素的结构体
typedef struct node_s {
  char *key;
  void *value;
  struct node_s *next;
} node_t;

// 定义散列表的结构体
typedef struct hashtable_s {
  int size;
  node_t **table;
} hashtable_t;

// 计算散列值的函数
unsigned int hash_func(hashtable_t *hashtable, char *key) {
  unsigned int hashval = 0;
  for (; *key != '\0'; key++) {
    hashval = *key + (hashval << 5) - hashval;
  }
  return hashval % hashtable->size;
}

// 创建并初始化一个散列表
hashtable_t *create_hashtable(int size) {
  hashtable_t *hashtable;

  if (size < 1) {
    return NULL;
  }

  hashtable = (hashtable_t *)malloc(sizeof(hashtable_t));
  hashtable->table = (node_t **)malloc(sizeof(node_t *) * size);
  for (int i = 0; i < size; i++) {
    hashtable->table[i] = NULL;
  }
  hashtable->size = size;

  return hashtable;
}

// 插入一个元素到散列表
int hashtable_insert(hashtable_t *hashtable, char *key, void *value) {
  node_t *new_node;
  unsigned int hashval = hash_func(hashtable, key);
  node_t *current_node = hashtable->table[hashval];

  while (current_node != NULL && strcmp(current_node->key, key) != 0) {
    current_node = current_node->next;
  }

  if (current_node != NULL) {  // 更新值
    current_node->value = value;
  } else {  // 插入新元素
    new_node = (node_t *)malloc(sizeof(node_t));
    new_node->key = strdup(key);
    new_node->value = value;
    new_node->next = hashtable->table[hashval];
    hashtable->table[hashval] = new_node;
  }

  return 0;
}

// 从散列表中查找一个元素
void *hashtable_search(hashtable_t *hashtable, char *key) {
  unsigned int hashval = hash_func(hashtable, key);
  node_t *current_node = hashtable->table[hashval];
  while (current_node != NULL && strcmp(current_node->key, key) != 0) {
    current_node = current_node->next;
  }
  if (current_node == NULL) {
    return NULL;
  } else {
    return current_node->value;
  }
}

// 从散列表中删除一个元素
int hashtable_delete(hashtable_t *hashtable, char *key) {
  unsigned int hashval = hash_func(hashtable, key);
  node_t *current_node = hashtable->table[hashval];
  node_t *prev_node = NULL;

  while (current_node != NULL && strcmp(current_node->key, key) != 0) {
    prev_node = current_node;
    current_node = current_node->next;
  }

  if (current_node == NULL) {
    return -1;  // 没有找到元素
  } else {
    if (prev_node == NULL) {  // 需要删除的元素位于链表头部
      hashtable->table[hashval] = current_node->next;
    } else {
      prev_node->next = current_node->next;
    }
    free(current_node->key);
    free(current_node);
    return 0;
  }
}

// 销毁散列表
void hashtable_destroy(hashtable_t *hashtable) {
  node_t *current_node, *temp_node;
  for (int i = 0; i < hashtable->size; i++) {
    current_node = hashtable->table[i];
    while (current_node != NULL) {  // 释放散列表中每个元素的内存
      temp_node = current_node;
      current_node = current_node->next;
      free(temp_node->key);
      free(temp_node);
    }
  }
  free(hashtable->table);
  free(hashtable);
  return;
}

示例

示例1:实现一个电话簿

首先定义电话簿的结构体:

typedef struct phonebook_s {
  hashtable_t *hashtable;
} phonebook_t;

然后实现添加联系人的方法:

int phonebook_add_contact(phonebook_t *phonebook, char *name, char *phone) {
  contact_t *contact;
  contact = (contact_t *)malloc(sizeof(contact_t));
  contact->name = strdup(name);
  contact->phone = strdup(phone);

  hashtable_insert(phonebook->hashtable, name, contact);

  return 0;
}

实现查找联系人的方法:

contact_t *phonebook_search_contact(phonebook_t *phonebook, char *name) {
  return (contact_t *)hashtable_search(phonebook->hashtable, name);
}

实现删除联系人的方法:

int phonebook_delete_contact(phonebook_t *phonebook, char *name) {
  return hashtable_delete(phonebook->hashtable, name);
}

示例2:实现身份证号码的校验

身份证号码的前17位是数字,最后一位可能是数字或字母X。我们可以使用散列表存储身份证前17位的数字与其对应的权值(权值就是每一位数字所对应的权值,包括各种因子的加权和),并且使用所得到的总和进行校验。

定义存储身份证号码权值的结构体:

typedef struct idcard_value_s {
  char *num;
  int value;
} idcard_value_t;

定义身份证校验的方法:

int check_idcard(char *idcard) {
  int i, j;
  int sum = 0;

  // 计算身份证前17位数字的校验和
  idcard_value_t values[17] = {
    {"1", 7}, {"0", 9}, {"x", 10}, {"9", 0}, {"8", 1}, {"7", 3},
    {"6", 5}, {"5", 8}, {"4", 0}, {"3", 2}, {"2", 4}, {"1", 6},
    {"0", 8}, {"x", 11}, {"9", 3}, {"8", 5}, {"7", 9}
  };
  int value;
  for (i = 0; i < 17; i++) {
    value = 0;
    for (j = 0; j < 17-i; j++) {
      value += (idcard[j] - '0') * pow(2, 17-i-j-1);
    }
    sum += values[i].value * (value % 11);
  }

  // 校验最后一位数字或字母是否正确
  char last_char = tolower(idcard[17]);
  int last_num;
  if (last_char == 'x') {
    last_num = 10;
  } else if (last_char >= '0' && last_char <= '9') {
    last_num = last_char - '0';
  } else {
    return 0;
  }
  if ((sum + last_num) % 11 == 1) {
    return 1;
  } else {
    return 0;
  }
}

上述代码中使用了pwo()函数,需要包含math.h头文件:

#include <math.h>

更多用法示例可以参考散列表的应用场景。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C语言写一个散列表 - Python技术站

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

相关文章

  • VScode上配置 c语言环境的图文教程

    下面我将为你提供VScode上配置C语言环境的详细图文教程,具体步骤如下: 第一步:安装C语言编译器 在配置C语言环境之前,我们需要安装C语言编译器。对于Windows用户,建议安装MinGW-w64。下载地址:http://mingw-w64.org/doku.php/download。选择对应的版本(32位或64位),下载后安装即可。对于Mac用户,可以…

    C 2023年5月22日
    00
  • 电脑开机蓝屏显示错误代码0xc0000034该怎么办?

    电脑开机蓝屏显示错误代码0xc0000034该怎么办? 在电脑开机时,有时候会遇到蓝屏错误,其中一个比较常见的错误代码是0xc0000034。这一错误代码通常与启动配置文件有关,可能是文件损坏或者缺失引起的。在这里,我们提供一些可能有效的解决方案。 方案一:使用Windows恢复环境 准备一张 Windows 安装盘或者 U 盘,将其插入电脑并启动电脑。 进…

    C 2023年5月23日
    00
  • C/C++实现个人收支系统的示例代码

    让我详细讲解一下“C/C++实现个人收支系统的示例代码”的完整攻略。 首先,我们需要了解个人收支系统的功能需求,一般来说,个人收支系统至少需要提供如下的功能: 记录收入支出的日期、金额和说明; 查询某一日期段内的收入和支出总额; 查询某一日期段内的收入和支出详情; 查询某一个时间点的余额; 导出收支记录。 接下来,我们可以按照模块拆分的方式逐一实现这些功能。…

    C 2023年5月23日
    00
  • 利用C++11原子量如何实现自旋锁详解

    当多个线程需要访问某个公共资源时,为了避免数据竞争(Data Race)和死锁(Lock),我们通常使用线程同步机制,其中自旋锁(SpinLock)就是其中一种。自旋锁是基于忙等待的一种锁,当一个线程在持有锁的时候,其他线程将会不停地“自旋”,也就是反复检查是否可以获得锁。在这种情况下,当前线程将会占用CPU时间片,从而耗费CPU的计算资源。 使用C++11…

    C 2023年5月23日
    00
  • C++如何实现定长内存池详解

    C++实现定长内存池的详细攻略如下: 什么是定长内存池 定长内存池是一种用于管理内存分配和释放的方法。相对于动态内存分配和释放,定长内存池可以更高效地管理内存,因为它不需要频繁地进行内存分配和释放操作,而是预先分配一块连续的内存空间,然后在此基础上进行内存管理。 定长内存池的实现方法 在C++中,我们可以使用标准库中的std::vector或者自己实现一个内…

    C 2023年5月23日
    00
  • C++控制台绘图头文件实例代码

    下面是对“C++控制台绘图头文件实例代码”的完整攻略: 1. 简介 在C++的控制台程序中,通过使用图形化绘图头文件,可以在控制台中绘制出各种图形。 2. 下载 在使用绘图头文件前,需要下载对应的库文件。 目前比较流行的库包括: graphics.h:Borland C++ 5.02自带的,不建议使用。 conio.h:Turbo C自带的,也不建议使用。 …

    C 2023年5月24日
    00
  • C语言中如何进行递归操作?

    C语言是一门支持递归的编程语言,在C语言中,我们可以使用函数递归实现一些重复性操作,减少代码冗余并提高代码可读性。下面是C语言中如何进行递归操作的完整攻略。 1. 什么是递归? 递归(Recursion)是指在函数体内调用函数本身,或者指在某个数据结构中使用指向自身的指针,以此来进行一系列的操作。递归通常用于解决一些针对于大规模同类问题的算法设计。 2. 如…

    C 2023年4月27日
    00
  • Java程序的逻辑控制和方法详解

    Java程序的逻辑控制和方法详解 什么是逻辑控制 在Java中,逻辑控制是指程序判断和执行语句的顺序、次数、循环和选择等。常用的逻辑控制语句有if、for、while等等。 if语句 if语句是最简单的逻辑控制语句,有条件地执行语句。if语句的基本格式为: if (condition) { statement(s) to be executed if con…

    C 2023年5月22日
    00
合作推广
合作推广
分享本页
返回顶部