学习内容
哈希表
包含键、哈希函数、哈希码
看了几个入门视频,哈希的操作大概就是一个加密和解密的过程,比如通过哈希函数将一个特定字符、数字等数据结构转化成一个参数,这个参数对应这个特定的数据结构,什么意思呢?就是每一次输入同一个数据都会返回同一个参数,输入不同的数据得到不同的参数。这就是加密的过程。那有没有可能导致不同输入对应同一个输出呢?答案是肯定的,称为冲突,后面再讲。那如何实现解密?解密的操作相当于在前面加密的基础上,将得到的参数再重新输入到哈希函数的反函数里面,然后对应反解出原来的数据,正常来说如果没有冲突的情况的话,反解出来的数据应该是一一对应的,也就是和原来的数据一模一样。这样就实现了加密和解密的过程,但哈希的运用远不止这些,还有更多其他的用法,比如排序?Any other,后面学到再用,然后在看视频的过程中也顺带看到了另外一种算法——桶排序(例如:基数排序/计数排序)
哈希表有几个基本概念
位置/槽:哈希表中用于储存键值对的索引的位置(类似数组元素下标)
哈希冲突:哈希表中几个键通过哈希函数映射到了同一个位置。那么应该如何解决?两种常用的方法
第一个是用链表来作为位置/槽,这样子所有映射到同一个位置的元素都可以存进去,然后再根据链表查找的方法来找出所需的元素。
第二个是开放寻址法,当发生冲突时,哈希函数会寻找哈希表另一个空闲的位置来储存当前元素。但这又牵扯出另一个问题,就是哈希表的内存可能小于数据个数,这样如果想要不冲突,就得能够实现哈希表的增长,称作动态扩容(动态规划问题,这里没有深究,但估计和malloc等函数干系很大)
哈希表的优点:虽然存在冲突这样的缺点(而且还能够避免,改善),哈希表还是有很多优点的
第一个就是实现对数据的快速访问,如果希望查找元素,与其遍历数组或者链表这样的长数据,不如使用哈希表中的键值对来实现(查找、插入、删除)高效访问。
第二个是能够实现高效存储,这一点是AI给出来的,说比平衡树等其他数据结构更节省内存?这个我倒没有学过...感觉要补起来还是蛮女娲的...但没关系,学生仔心中的信念:坚持就是胜利...
哈希表广泛应用:快速查找、插入和删除,如数据库索引、缓存实现、符号表等。
总的来说哈希表和核心就在于找到一个合适的哈希函数,能够保证能一一映射,而且时间、空间复杂度(这个我也没专门学过,但你知道的,我说术语还是蛮头头是道的)不会大于一般的简单算法,能够实现对数据存数的高效访问。
桶排序
看视频牵扯出来的桶排序,简单说来就是有几个不同的桶,相同的数据类型,比如数组,比如链表,然后把希望存储或者希望实现的操作在每个桶中先实现,再合起来。比如希望对二十个数进行排序,这样可以分成四个桶,可以先找出二十个数的最大最小值,然后确定桶的区间,比如是前二十个自然数,然后就可以分成四个桶——[0,4][5,9][10,14][15,19],然后先把20个乱序的数存进对应区间的桶,然后在桶内排好序,然后再直接放到一起,这样就可以得到20个完整排好序的20个数,可以打打降低时间复杂度和空间复杂度(当然也是视频讲的...我还是没法理解,也许明天会抽空理解理解什么叫复杂度...)。桶排序看起来是一种很高端的排序方法(相比我以前会的那种冒泡排序..),然后视频还讲到,有两种特殊的桶排序,一种是基数排序,就是按照基数,10,100,1000...(10为基数)这样,或者计数排序,就是整数为分界区间。但桶排序也有一个缺点,就是分布问题,如果20个数只有1个0,1个19,剩下18个都集中在8~12,这样子我们虽然建立了四个桶,但实际用到的只有一两个桶,而且这一两个桶的大小还得接近20,也就是原始数据长度,当数据量大起来的时候会造成极大的浪费......具体怎么解决呢?....这个也不懂,但我写在这先给自己一个潜意识,以后会回来的吧(感动自己bushi)。
代码实现
C语言
C语言库中没有自带的哈希表结构,需要自己定义,或者调用第三方库。然后这里我不写了...没错,就是不会。但可以贴上AI给出的参考代码。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TABLE_SIZE 10
// 定义哈希表中每个元素的结构体
typedef struct Entry {
char *key;
int value;
struct Entry *next;
} Entry;
// 定义哈希表的结构体
typedef struct HashTable {
Entry *entries[TABLE_SIZE];
} HashTable;
// 哈希函数,这里简单地使用字符串的ASCII值之和
unsigned int hash(char *key) {
unsigned int hashValue = 0;
while (*key) {
hashValue = hashValue * 31 + (*key++);
}
return hashValue % TABLE_SIZE;
}
// 插入元素到哈希表
void insert(HashTable *ht, char *key, int value) {
unsigned int index = hash(key);
Entry *entry = malloc(sizeof(Entry));
entry->key = strdup(key); // 复制键
entry->value = value;
entry->next = ht->entries[index]; // 链表的头插法
ht->entries[index] = entry;
}
// 查找哈希表中的元素
int *search(HashTable *ht, char *key) {
unsigned int index = hash(key);
Entry *entry = ht->entries[index];
while (entry) {
if (strcmp(entry->key, key) == 0) {
return &entry->value;
}
entry = entry->next;
}
return NULL;
}
// 删除哈希表中的元素
void remove(HashTable *ht, char *key) {
unsigned int index = hash(key);
Entry *current = ht->entries[index];
Entry *prev = NULL;
while (current) {
if (strcmp(current->key, key) == 0) {
if (prev) {
prev->next = current->next;
} else {
ht->entries[index] = current->next;
}
free(current->key);
free(current);
return;
}
prev = current;
current = current->next;
}
}
int main() {
HashTable ht;
memset(&ht, 0, sizeof(ht)); // 初始化哈希表
// 插入元素
insert(&ht, "apple", 100);
insert(&ht, "banana", 200);
insert(&ht, "cherry", 300);
// 查找元素
int *value = search(&ht, "banana");
if (value) {
printf("Found 'banana' with value: %d\n", *value);
} else {
printf("'banana' not found\n");
}
// 删除元素
remove(&ht, "banana");
// 再次查找被删除的元素
value = search(&ht, "banana");
if (value) {
printf("Found 'banana' with value: %d\n", *value);
} else {
printf("'banana' not found\n");
}
return 0;
}
代码里不涉及冲突解决方法的问题...动态扩容之类的。
学习总结
最开始今天是打算刷一道力扣的,但是,进去就看到用的URL码,什么加密解密...根本没思路,也看不懂。(我还是按照通过率降序刷题顺序),想不明白为什么通过率这么高,然后果断打开题解区,官方给出的三种解法里就有一种用到了哈希表,看不懂。然后往下翻翻就看到了一些两行解答...直接返回原来的URL...怪不得通过率这么高了...但我认为这样刷题一点意义也没有,既然这道题正常解对于现在的我太难了,那我一步一步走行不行...
好,打开AI开始学习哈希表....生成一段字符乱码...看不懂,去看视频,然后看了好几个才勉强明白基本思想(by the way数据算法也太难了...),基本思想就是函数映射的观念,最理想的状态就是一一对应,不会出现一对多或者多对一,但不保证。事实上所有的算法都无法保证一定正确,都停留来理论,和尚未发现BUG的实际。但是现在也有解决方法,好理解就是动态扩容,强行一一对应,函数值域不够就扩展值域(应该是这个思想吧)。然后就莫名看到弹幕很多刷桶排序的,就去看了相关视频,好像还真是差不多...也是对数据进行映射到桶里,然后桶里先进行简单算法,叠加后实现大数据的算法实现。不知道桶里能不能再有桶,(类似树?我也没学过,我猜的,树应该就像桶里的桶吧哈哈哈哈,层层简化,最后只需要对每个小分支进行处理合起来就是大工程!),哈哈只能说,又学到一个新的算法。
那今天到这也是结束了,也是学了两小时,虽然不是满满当当吧,中间刷了半小时视频,主要打开B站停留在主页是真的忍不住...但也算有收获?其中也体会到了科班学习的困难程度...什么数据结构,什么算法,什么数据库...我现在连这一个数据结构都弄了一晚上...唉
但没关系,既然选择了自学,就只有一个信念:我在变强!我在与别人拉开差距!如果连科班的困难都吃不下来,凭什么比科班强大?加油!...