前言
好久不见老朋友,初次见面新朋友,失踪人口回来啦。最近忙于学业没有更新动静so sorry,不过也正是因为被各种任务压着才让我想起,“为什么不去找找有没有人为我们分享这些内容呢?”,于是又转念想到,好像我曾经开了个系列坑是用来分享干货的来着?
这不又到期末了嘛,想必不少同学还在为数据结构这块内容痛苦着,咱也就来为各位的及格线奋斗史尽一点绵薄之力,若有帮助还请三连支持哦~,你们的反馈将成为我创作的巨大动力~
代码
废话不多说,直接上代码。
list.h
#ifndef _LIST_H_
#define _LIST_H_
//链式实现
struct list_node;
struct list;
//初始化线性表
struct list* list_create();
//销毁回收线性表
void list_destroy(struct list** list);
//查询节点个数
int list_count(const struct list*);
//查空
int list_isempty(const struct list*);
//清空
void list_clear(struct list*);
//添加
void list_set(struct list*, int key, int data, bool NX);
//删除
void list_remove(struct list* list, int key);
//获取
int list_get(const struct list* list, int key);
#endif
hashtable.h
#ifndef _HASHTABLE_H_
#define _HASHTABLE_H_
#include "list.h";
struct hashtable;
//初始化
struct hashtable* hs_init();
//回收
void hs_free(struct hashtable** h);
//哈希算法
int hash(int key);
//设置
void hs_insert(struct hashtable* h, int key, int value, bool NX);
//删除
void hs_remove(struct hashtable* h, int key);
//清空
void hs_clear(struct hashtable* h);
//访问
int hs_get(const struct hashtable* h, int key);
//查询元素个数
int hs_count(const struct hashtable* h);
#endif
list.cpp
#include "list.h"
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
//链式实现
struct list_node {
int key;
int value;
struct list_node* next;
};
struct list {
struct list_node* head;
int count;
};
list* list_create()
{
list* new_list = (list*)malloc(sizeof(list));
assert(new_list != nullptr);
new_list->count = 0;
new_list->head = nullptr;
return new_list;
}
void list_destroy(struct list** list)
{
assert(*list != nullptr);
//先遍历释放链表中的元素
while ((*list)->head != nullptr) {
list_node* new_head = (*list)->head->next;
free((*list)->head);
(*list)->head = new_head;
}
free(*list);
*list = nullptr;
}
int list_count(const list* list)
{
assert(list != nullptr);
return list->count;
}
bool list_isempty(const list* list)
{
assert(list != nullptr);
if (list->head == nullptr) return true;
return false;
}
void list_clear(list* list)
{
assert(list != nullptr);
//先顺序释放链表中的元素
while (list->head != nullptr) {
list_node* tmp = list->head;
list->head = list->head->next;
free(tmp);
}
list->count = 0;
return;
//和destroy唯一的区别无非就是不用释放表
}
//对用作拉链法的list进行改造,永远进行头插法,且可以覆盖原有的同key数据
void list_set(list* list, int key, int data, bool NX)
{
assert(list != nullptr);
//先进行遍历查找是否已有key,有就得根据NX决定是否覆盖
list_node* tmp = list->head;
while (tmp) {
if (tmp->key == key) {
if (NX) {
return;
}
else {
tmp->value = data;
return;
}
}
tmp = tmp->next;
}
list_node* new_head = (list_node*)malloc(sizeof(list_node));
new_head->key = key;
new_head->value = data;
//如果初始表为空
if (list->count == 0) {
list->head = new_head;
++list->count;
new_head->next = nullptr;
return;
}
new_head->next = list->head;
list->head = new_head;
++list->count;
return;
}
void list_remove(list* list, int key)
{
assert(list != nullptr);
list_node* prev = nullptr;
list_node* now = list->head;
list_node* last = now->next;
while (now) {
//若为删除头节点,需要特殊处理
if (now->key == key && now == list->head) {
//减少节点个数
--list->count;
//更新头节点
list->head = list->head->next;
//回收操作
free(now);
now = nullptr;
return;
}
//若为删除中间或尾部节点,则需要将前驱与后继进行连接处理
if (now->key == key) {
list_node* tmp = now;
//重新连接
prev->next = last;
//删除节点
free(tmp);
tmp = nullptr;
//减少个数
--list->count;
return;
}
}
return;
}
int list_get(const list* list, int key)
{
//遍历查找
list_node* tmp = list->head;
while (tmp) {
if (tmp->key == key) {
return tmp->value;
}
tmp = tmp->next;
}
printf("error: the data is invalid\n");
return 0;
}
hashtable.cpp
#include "hashtable.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
const int HASH_NUM = 17;
struct hashtable {
struct list* table[HASH_NUM];
};
hashtable* hs_init() {
hashtable* new_hashtable = (hashtable*)malloc(sizeof(hashtable));
assert(new_hashtable != nullptr);
for (int i = 0; i < HASH_NUM; ++i) {
new_hashtable->table[i] = list_create();
assert(new_hashtable->table[i] != nullptr);
}
printf("operate init success\n");
return new_hashtable;
}
void hs_free(hashtable** h)
{
assert(h != nullptr);
for (int i = 0; i < HASH_NUM; ++i) {
if ((*h)->table[i] != nullptr) {
list_destroy(&((*h)->table[i]));
}
}
free(*h);
*h = nullptr;
printf("operate free success\n");
return;
}
int hash(int key)
{
return key % HASH_NUM;
}
void hs_insert(struct hashtable* h, int key, int value, bool NX)
{
int index = hash(key);
list_set(h->table[index], key, value, NX);
printf("operate insert success\n");
return;
}
void hs_remove(hashtable* h, int key)
{
int index = hash(key);
list_remove(h->table[index], key);
printf("operate remove success\n");
}
void hs_clear(hashtable* h)
{
for (int i = 0; i < HASH_NUM; ++i) {
list_clear(h->table[i]);
}
printf("operate clear success\n");
return;
}
int hs_get(const hashtable* h, int key)
{
int index = hash(key);
return list_get(h->table[index], key);
}
int hs_count(const hashtable* h)
{
assert(h != nullptr);
int count = 0;
for (int i = 0; i < HASH_NUM; ++i) {
count = count + list_count(h->table[i]);
}
return count;
}
main.cpp
#include <stdio.h>
#include "hashtable.h"
int main() {
hashtable* test_h = hs_init();
//元素个数获取测试
printf("The count of the hashtable is %d\n", hs_count(test_h));
//分割线
printf("----------------------------------\n");
//设置测试,不可覆盖
hs_insert(test_h, 49, 10, true);
hs_insert(test_h, 64, 20, true);
hs_insert(test_h, 1, 30, true);
hs_insert(test_h, 13, 40, true);
hs_insert(test_h, 666, 50, true);
hs_insert(test_h, 87, 60, true);
//分割线
printf("----------------------------------\n");
//元素个数获取测试
printf("The count of the hashtable is %d\n", hs_count(test_h));
//分割线
printf("----------------------------------\n");
int key;
//元素获取测试
printf("Input key number to get: ");
scanf_s("%d",&key);
printf("The data of %d(key) is %d\n", key, hs_get(test_h, key));
//分割线
printf("----------------------------------\n");
//设置测试,可覆盖
hs_insert(test_h, 49, 70, false);
hs_insert(test_h, 64, 80, false);
hs_insert(test_h, 1, 90, false);
hs_insert(test_h, 13, 100, false);
hs_insert(test_h, 666, 110, false);
hs_insert(test_h, 87, 120, false);
//分割线
printf("----------------------------------\n");
//元素个数获取测试
printf("The count of the hashtable is %d\n", hs_count(test_h));
//分割线
printf("----------------------------------\n");
//元素获取测试
printf("Input key number to get: ");
scanf_s("%d", &key);
printf("The data of %d(key) is %d\n", key, hs_get(test_h, key));
//分割线
printf("----------------------------------\n");
//元素删除测试
printf("Input key number to remove: ");
scanf_s("%d", &key);
hs_remove(test_h, key);
//分割线
printf("----------------------------------\n");
//元素获取测试
printf("Input key number to get: ");
scanf_s("%d", &key);
printf("The data of %d(key) is %d\n", key, hs_get(test_h, key));
//分割线
printf("----------------------------------\n");
//元素个数获取测试
printf("The count of the hashtable is %d\n", hs_count(test_h));
//分割线
printf("----------------------------------\n");
//清空测试
hs_clear(test_h);
//分割线
printf("----------------------------------\n");
//元素个数获取测试
printf("The count of the hashtable is %d\n", hs_count(test_h));
//分割线
printf("----------------------------------\n");
hs_free(&test_h);
return 0;
}
运行结果
讲解
别看内容很多,实际上逻辑很简单,无非还是那一套crud而已,有STL容器或Redis使用经验的同学或许已经对这些操作十分熟悉了,在此仅对几个特殊的点进行讲解:
①本哈希表采用的是拉链法来解决哈希冲突的;
②容量与哈希算法用定位数为HASH_NUM,可根据需要进行修改,最好为质数,可以最大程度上地防止过多元素产生哈希冲突;
③hs_insert为设置功能,可以根据NX参数来决定是否对已有元素进行覆盖;
④hs_get默认返回值为0,若访问的为空键值对,则会进行报错,但并不终止程序;
⑤main.cpp为简单的功能测试用程序,不放心的可以自行进行设计测试,需要注意的是,本程序是在Visual Studio 2022环境下编写的,scanf需要使用其安全模式scanf_s,否则需要自行进行项目预处理参数的修改或使用其他编译器;
⑥为了让更多同学能够无障碍读码,本哈希表采用C语言面向过程编程实现,如需要更改为其他面向对象编程的语言如C++、Java请自行参考。
惯例啰嗦
数据结构与算法是编程底力的重中之重,也是难啃的硬骨头,如果还有想了解的内容可以在评论区留言,或许下一期会给予参考哦~。前提是我还能想起账号密码的话