240513.学习日志——数据结构与算法.哈希表(Hash Table)

学习内容

哈希表

包含键、哈希函数、哈希码

看了几个入门视频,哈希的操作大概就是一个加密和解密的过程,比如通过哈希函数将一个特定字符、数字等数据结构转化成一个参数,这个参数对应这个特定的数据结构,什么意思呢?就是每一次输入同一个数据都会返回同一个参数,输入不同的数据得到不同的参数。这就是加密的过程。那有没有可能导致不同输入对应同一个输出呢?答案是肯定的,称为冲突,后面再讲。那如何实现解密?解密的操作相当于在前面加密的基础上,将得到的参数再重新输入到哈希函数的反函数里面,然后对应反解出原来的数据,正常来说如果没有冲突的情况的话,反解出来的数据应该是一一对应的,也就是和原来的数据一模一样。这样就实现了加密和解密的过程,但哈希的运用远不止这些,还有更多其他的用法,比如排序?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站停留在主页是真的忍不住...但也算有收获?其中也体会到了科班学习的困难程度...什么数据结构,什么算法,什么数据库...我现在连这一个数据结构都弄了一晚上...唉

但没关系,既然选择了自学,就只有一个信念:我在变强!我在与别人拉开差距!如果连科班的困难都吃不下来,凭什么比科班强大?加油!... 

 

 

  • 20
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
哈希表是一种基于哈希函数进行快速查找的数据结构,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。哈希表的设计思路如下: 1. 哈希函数的设计:哈希函数是哈希表的核心,它将关键字映射到哈希表中的位置。一个好的哈希函数应该具有以下特点: - 映射范围广:哈希函数应该将关键字均匀地映射到哈希表中的位置,避免出现大量的哈希冲突。 - 计算速度快:哈希函数的计算速度应该尽可能快,以提高哈希表的访问速度。 - 低冲突率:哈希函数应该尽可能地避免哈希冲突,以提高哈希表的访问效率。 2. 哈希冲突的解决:由于哈希函数的映射范围是有限的,所以不同的关键字可能会映射到同一个位置,这就是哈希冲突。哈希冲突的解决方法有以下两种: - 链地址法:将哈希表中的每个位置都连接一个链表,当发生哈希冲突时,将新的关键字插入到链表的末尾。 - 开放地址法:当发生哈希冲突时,通过某种算法找到哈希表中的下一个空位置,将新的关键字插入到该位置。 3. 哈希表的增删查改操作:哈希表的增删查改操作都需要先通过哈希函数找到关键字在哈希表中的位置,然后再进行相应的操作。具体操作如下: - 插入操作:将新的关键字插入到哈希表中的对应位置,如果发生哈希冲突,则按照链地址法或开放地址法进行解决。 - 删除操作:将关键字从哈希表中对应位置删除,如果该位置上有链表,则需要遍历链表找到对应的关键字进行删除。 - 查找操作:通过哈希函数找到关键字在哈希表中的位置,如果该位置上有链表,则需要遍历链表找到对应的关键字进行查找。 - 修改操作:通过哈希函数找到关键字在哈希表中的位置,如果该位置上有链表,则需要遍历链表找到对应的关键字进行修改。 下面是一个使用链地址法实现的哈希表的Python代码示例: ```python class ListNode: def __init__(self, key=None, value=None): self.key = key self.value = value self.next = None class MyHashMap: def __init__(self): self.size = 1000 self.table = [None] * self.size def _hash(self, key): return key % self.size def put(self, key, value): index = self._hash(key) if not self.table[index]: self.table[index] = ListNode(key, value) else: node = self.table[index] while node: if node.key == key: node.value = value return if not node.next: break node = node.next node.next = ListNode(key, value) def get(self, key): index = self._hash(key) node = self.table[index] while node: if node.key == key: return node.value node = node.next return -1 def remove(self, key): index = self._hash(key) node = prev = self.table[index] if not node: return if node.key == key: self.table[index] = node.next else: node = node.next while node: if node.key == key: prev.next = node.next break node, prev = node.next, prev.next ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值