diff --git a/README.md b/README.md index 2bebeff4..3a450f25 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ - 源代码可一键运行,帮助读者在实践练习中提升编程技能,了解算法工作原理和数据结构底层实现; - 鼓励读者互助学习,提问与评论通常可在两日内得到回复; -若本书对您有所帮助,请点个 Star :star: 支持一下,谢谢! +若本书对您有所帮助,请在页面右上角点个 Star :star: 支持一下,谢谢! ## 推荐语 diff --git a/codes/c/chapter_hashing/array_hash_map.c b/codes/c/chapter_hashing/array_hash_map.c index 9b2a0cf4..e55f4dbb 100644 --- a/codes/c/chapter_hashing/array_hash_map.c +++ b/codes/c/chapter_hashing/array_hash_map.c @@ -10,24 +10,24 @@ # define HASH_MAP_DEFAULT_SIZE 100 /* 键值对 int->string */ -struct Entry { +struct pair { int key; char *val; }; -typedef struct Entry Entry; +typedef struct pair pair; /* 用于表示键值对、键、值的集合 */ -struct MapSet { +struct mapSet { void *set; int len; }; -typedef struct MapSet MapSet; +typedef struct mapSet mapSet; /* 基于数组简易实现的哈希表 */ struct ArrayHashMap { - Entry *buckets[HASH_MAP_DEFAULT_SIZE]; + pair *buckets[HASH_MAP_DEFAULT_SIZE]; }; typedef struct ArrayHashMap ArrayHashMap; @@ -47,7 +47,7 @@ int hashFunc(int key) { /* 查询操作 */ const char *get(const ArrayHashMap *d, const int key) { int index = hashFunc(key); - const Entry *pair = d->buckets[index]; + const pair *pair = d->buckets[index]; if (pair == NULL) return NULL; return pair->val; @@ -55,7 +55,7 @@ const char *get(const ArrayHashMap *d, const int key) { /* 添加操作 */ void put(ArrayHashMap *d, const int key, const char *val) { - Entry *pair = malloc(sizeof(Entry)); + pair *pair = malloc(sizeof(pair)); pair->key = key; pair->val = malloc(strlen(val) + 1); strcpy(pair->val, val); @@ -73,19 +73,19 @@ void removeItem(ArrayHashMap *d, const int key) { } /* 获取所有键值对 */ -void entrySet(ArrayHashMap *d, MapSet *set) { - Entry *entries; +void pairSet(ArrayHashMap *d, mapSet *set) { + pair *entries; int i = 0, index = 0; int total = 0; - /* 统计有效 entry 数量 */ + /* 统计有效键值对数量 */ for (i = 0; i < HASH_MAP_DEFAULT_SIZE; i++) { if (d->buckets[i] != NULL) { total++; } } - entries = malloc(sizeof(Entry) * total); + entries = malloc(sizeof(pair) * total); for (i = 0; i < HASH_MAP_DEFAULT_SIZE; i++) { if (d->buckets[i] != NULL) { entries[index].key = d->buckets[i]->key; @@ -100,12 +100,12 @@ void entrySet(ArrayHashMap *d, MapSet *set) { } /* 获取所有键 */ -void keySet(ArrayHashMap *d, MapSet *set) { +void keySet(ArrayHashMap *d, mapSet *set) { int *keys; int i = 0, index = 0; int total = 0; - /* 统计有效 entry 数量 */ + /* 统计有效键值对数量 */ for (i = 0; i < HASH_MAP_DEFAULT_SIZE; i++) { if (d->buckets[i] != NULL) { total++; @@ -125,12 +125,12 @@ void keySet(ArrayHashMap *d, MapSet *set) { } /* 获取所有值 */ -void valueSet(ArrayHashMap *d, MapSet *set) { +void valueSet(ArrayHashMap *d, mapSet *set) { char **vals; int i = 0, index = 0; int total = 0; - /* 统计有效 entry 数量 */ + /* 统计有效键值对数量 */ for (i = 0; i < HASH_MAP_DEFAULT_SIZE; i++) { if (d->buckets[i] != NULL) { total++; @@ -152,9 +152,9 @@ void valueSet(ArrayHashMap *d, MapSet *set) { /* 打印哈希表 */ void print(ArrayHashMap *d) { int i; - MapSet set; - entrySet(d, &set); - Entry *entries = (Entry*) set.set; + mapSet set; + pairSet(d, &set); + pair *entries = (pair*) set.set; for (i = 0; i < set.len; i++) { printf("%d -> %s\n", entries[i].key, entries[i].val); } @@ -193,7 +193,7 @@ int main() { printf("\n遍历键值对 Key->Value\n"); print(map); - MapSet set; + mapSet set; keySet(map, &set); int *keys = (int*) set.set; diff --git a/codes/cpp/chapter_hashing/array_hash_map.cpp b/codes/cpp/chapter_hashing/array_hash_map.cpp index b52db173..ca8fa6f4 100644 --- a/codes/cpp/chapter_hashing/array_hash_map.cpp +++ b/codes/cpp/chapter_hashing/array_hash_map.cpp @@ -6,12 +6,12 @@ #include "../utils/common.hpp" -/* 键值对 int->String */ -struct Entry { +/* 键值对 */ +struct Pair { public: int key; string val; - Entry(int key, string val) { + Pair(int key, string val) { this->key = key; this->val = val; } @@ -20,12 +20,12 @@ struct Entry { /* 基于数组简易实现的哈希表 */ class ArrayHashMap { private: - vector buckets; + vector buckets; public: ArrayHashMap() { // 初始化数组,包含 100 个桶 - buckets = vector(100); + buckets = vector(100); } ~ArrayHashMap() { @@ -45,7 +45,7 @@ class ArrayHashMap { /* 查询操作 */ string get(int key) { int index = hashFunc(key); - Entry *pair = buckets[index]; + Pair *pair = buckets[index]; if (pair == nullptr) return nullptr; return pair->val; @@ -53,7 +53,7 @@ class ArrayHashMap { /* 添加操作 */ void put(int key, string val) { - Entry *pair = new Entry(key, val); + Pair *pair = new Pair(key, val); int index = hashFunc(key); buckets[index] = pair; } @@ -67,20 +67,20 @@ class ArrayHashMap { } /* 获取所有键值对 */ - vector entrySet() { - vector entrySet; - for (Entry *pair : buckets) { + vector pairSet() { + vector pairSet; + for (Pair *pair : buckets) { if (pair != nullptr) { - entrySet.push_back(pair); + pairSet.push_back(pair); } } - return entrySet; + return pairSet; } /* 获取所有键 */ vector keySet() { vector keySet; - for (Entry *pair : buckets) { + for (Pair *pair : buckets) { if (pair != nullptr) { keySet.push_back(pair->key); } @@ -91,7 +91,7 @@ class ArrayHashMap { /* 获取所有值 */ vector valueSet() { vector valueSet; - for (Entry *pair : buckets) { + for (Pair *pair : buckets) { if (pair != nullptr) { valueSet.push_back(pair->val); } @@ -101,7 +101,7 @@ class ArrayHashMap { /* 打印哈希表 */ void print() { - for (Entry *kv : entrySet()) { + for (Pair *kv : pairSet()) { cout << kv->key << " -> " << kv->val << endl; } } @@ -135,7 +135,7 @@ int main() { /* 遍历哈希表 */ cout << "\n遍历键值对 Key->Value" << endl; - for (auto kv : map.entrySet()) { + for (auto kv : map.pairSet()) { cout << kv->key << " -> " << kv->val << endl; } diff --git a/codes/cpp/chapter_hashing/hash_map_chaining.cpp b/codes/cpp/chapter_hashing/hash_map_chaining.cpp new file mode 100644 index 00000000..03ebe16b --- /dev/null +++ b/codes/cpp/chapter_hashing/hash_map_chaining.cpp @@ -0,0 +1,149 @@ +/** + * File: hash_map_chaining.cpp + * Created Time: 2023-06-13 + * Author: Krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* 键值对 */ +struct Pair { + public: + int key; + string val; + Pair(int key, string val) { + this->key = key; + this->val = val; + } +}; + +/* 链式地址哈希表 */ +class HashMapChaining { + private: + int size; // 键值对数量 + int capacity; // 哈希表容量 + double loadThres; // 触发扩容的负载因子阈值 + int extendRatio; // 扩容倍数 + vector> buckets; // 桶数组 + + public: + /* 构造方法 */ + HashMapChaining() : size(0), capacity(4), loadThres(2.0 / 3), extendRatio(2) { + buckets.resize(capacity); + } + + /* 哈希函数 */ + int hashFunc(int key) { + return key % capacity; + } + + /* 负载因子 */ + double loadFactor() { + return (double)size / (double)capacity; + } + + /* 查询操作 */ + string get(int key) { + int index = hashFunc(key); + // 遍历桶,若找到 key 则返回对应 val + for (Pair *pair : buckets[index]) { + if (pair->key == key) { + return pair->val; + } + } + // 若未找到 key 则返回 nullptr + return nullptr; + } + + /* 添加操作 */ + void put(int key, string val) { + // 当负载因子超过阈值时,执行扩容 + if (loadFactor() > loadThres) { + extend(); + } + int index = hashFunc(key); + // 遍历桶,若遇到指定 key ,则更新对应 val 并返回 + for (Pair *pair : buckets[index]) { + if (pair->key == key) { + pair->val = val; + return; + } + } + // 若无该 key ,则将键值对添加至尾部 + buckets[index].push_back(new Pair(key, val)); + size++; + } + + /* 删除操作 */ + void remove(int key) { + int index = hashFunc(key); + auto &bucket = buckets[index]; + // 遍历桶,从中删除键值对 + for (int i = 0; i < bucket.size(); i++) { + if (bucket[i]->key == key) { + Pair *tmp = bucket[i]; + bucket.erase(bucket.begin() + i); // 从中删除键值对 + delete tmp; // 释放内存 + size--; + return; + } + } + } + + /* 扩容哈希表 */ + void extend() { + // 暂存原哈希表 + vector> bucketsTmp = buckets; + // 初始化扩容后的新哈希表 + capacity *= extendRatio; + buckets.clear(); + buckets.resize(capacity); + size = 0; + // 将键值对从原哈希表搬运至新哈希表 + for (auto &bucket : bucketsTmp) { + for (Pair *pair : bucket) { + put(pair->key, pair->val); + } + } + } + + /* 打印哈希表 */ + void print() { + for (auto &bucket : buckets) { + cout << "["; + for (Pair *pair : bucket) { + cout << pair->key << " -> " << pair->val << ", "; + } + cout << "]\n"; + } + } +}; + +/* Driver Code */ +int main() { + /* 初始化哈希表 */ + HashMapChaining map = HashMapChaining(); + + /* 添加操作 */ + // 在哈希表中添加键值对 (key, value) + map.put(12836, "小哈"); + map.put(15937, "小啰"); + map.put(16750, "小算"); + map.put(13276, "小法"); + map.put(10583, "小鸭"); + cout << "\n添加完成后,哈希表为\nKey -> Value" << endl; + map.print(); + + /* 查询操作 */ + // 向哈希表输入键 key ,得到值 value + string name = map.get(13276); + cout << "\n输入学号 13276 ,查询到姓名 " << name << endl; + + /* 删除操作 */ + // 在哈希表中删除键值对 (key, value) + map.remove(12836); + cout << "\n删除 12836 后,哈希表为\nKey -> Value" << endl; + map.print(); + + return 0; +} diff --git a/codes/cpp/chapter_hashing/hash_map_open_addressing.cpp b/codes/cpp/chapter_hashing/hash_map_open_addressing.cpp new file mode 100644 index 00000000..43727311 --- /dev/null +++ b/codes/cpp/chapter_hashing/hash_map_open_addressing.cpp @@ -0,0 +1,166 @@ +/** + * File: hash_map_open_addressing.cpp + * Created Time: 2023-06-13 + * Author: Krahets (krahets@163.com) + */ + +#include "../utils/common.hpp" + +/* 键值对 */ +struct Pair { + int key; + string val; + + Pair(int k, string v) : key(k), val(v) { + } +}; + +/* 开放寻址哈希表 */ +class HashMapOpenAddressing { + private: + int size; // 键值对数量 + int capacity; // 哈希表容量 + double loadThres; // 触发扩容的负载因子阈值 + int extendRatio; // 扩容倍数 + vector buckets; // 桶数组 + Pair *removed; // 删除标记 + + public: + /* 构造方法 */ + HashMapOpenAddressing() { + // 构造方法 + size = 0; + capacity = 4; + loadThres = 2.0 / 3.0; + extendRatio = 2; + buckets = vector(capacity, nullptr); + removed = new Pair(-1, "-1"); + } + + /* 哈希函数 */ + int hashFunc(int key) { + return key % capacity; + } + + /* 负载因子 */ + double loadFactor() { + return static_cast(size) / capacity; + } + + /* 查询操作 */ + string get(int key) { + int index = hashFunc(key); + // 线性探测,从 index 开始向后遍历 + for (int i = 0; i < capacity; i++) { + // 计算桶索引,越过尾部返回头部 + int j = (index + i) % capacity; + // 若遇到空桶,说明无此 key ,则返回 nullptr + if (buckets[j] == nullptr) + return nullptr; + // 若遇到指定 key ,则返回对应 val + if (buckets[j]->key == key && buckets[j] != removed) + return buckets[j]->val; + } + return nullptr; + } + + /* 添加操作 */ + void put(int key, string val) { + // 当负载因子超过阈值时,执行扩容 + if (loadFactor() > loadThres) + extend(); + int index = hashFunc(key); + // 线性探测,从 index 开始向后遍历 + for (int i = 0; i < capacity; i++) { + // 计算桶索引,越过尾部返回头部 + int j = (index + i) % capacity; + // 若遇到空桶、或带有删除标记的桶,则将键值对放入该桶 + if (buckets[j] == nullptr || buckets[j] == removed) { + buckets[j] = new Pair(key, val); + size += 1; + return; + } + // 若遇到指定 key ,则更新对应 val + if (buckets[j]->key == key) { + buckets[j]->val = val; + return; + } + } + } + + /* 删除操作 */ + void remove(int key) { + int index = hashFunc(key); + // 线性探测,从 index 开始向后遍历 + for (int i = 0; i < capacity; i++) { + // 计算桶索引,越过尾部返回头部 + int j = (index + i) % capacity; + // 若遇到空桶,说明无此 key ,则直接返回 + if (buckets[j] == nullptr) + return; + // 若遇到指定 key ,则标记删除并返回 + if (buckets[j]->key == key) { + delete buckets[j]; // 释放内存 + buckets[j] = removed; + size -= 1; + return; + } + } + } + + /* 扩容哈希表 */ + void extend() { + // 暂存原哈希表 + vector bucketsTmp = buckets; + // 初始化扩容后的新哈希表 + capacity *= extendRatio; + buckets = vector(capacity, nullptr); + size = 0; + // 将键值对从原哈希表搬运至新哈希表 + for (Pair *pair : bucketsTmp) { + if (pair != nullptr && pair != removed) { + put(pair->key, pair->val); + } + } + } + + /* 打印哈希表 */ + void print() { + for (auto &pair : buckets) { + if (pair != nullptr) { + cout << pair->key << " -> " << pair->val << endl; + } else { + cout << "nullptr" << endl; + } + } + } +}; + +/* Driver Code */ +int main() { + /* 初始化哈希表 */ + HashMapOpenAddressing map = HashMapOpenAddressing(); + + /* 添加操作 */ + // 在哈希表中添加键值对 (key, value) + map.put(12836, "小哈"); + map.put(15937, "小啰"); + map.put(16750, "小算"); + map.put(13276, "小法"); + map.put(10583, "小鸭"); + cout << "\n添加完成后,哈希表为\nKey -> Value" << endl; + map.print(); + + /* 查询操作 */ + // 向哈希表输入键 key ,得到值 value + string name = map.get(13276); + cout << "\n输入学号 13276 ,查询到姓名 " << name << endl; + + /* 删除操作 */ + // 在哈希表中删除键值对 (key, value) + map.remove(16750); + cout << "\n删除 16750 后,哈希表为\nKey -> Value" << endl; + map.print(); + + return 0; +} diff --git a/codes/csharp/chapter_graph/graph_adjacency_list.cs b/codes/csharp/chapter_graph/graph_adjacency_list.cs index ae66eec1..53bca147 100644 --- a/codes/csharp/chapter_graph/graph_adjacency_list.cs +++ b/codes/csharp/chapter_graph/graph_adjacency_list.cs @@ -68,11 +68,11 @@ public class GraphAdjList { /* 打印邻接表 */ public void print() { Console.WriteLine("邻接表 ="); - foreach (KeyValuePair> entry in adjList) { + foreach (KeyValuePair> pair in adjList) { List tmp = new List(); - foreach (Vertex vertex in entry.Value) + foreach (Vertex vertex in pair.Value) tmp.Add(vertex.val); - Console.WriteLine(entry.Key.val + ": [" + string.Join(", ", tmp) + "],"); + Console.WriteLine(pair.Key.val + ": [" + string.Join(", ", tmp) + "],"); } } } diff --git a/codes/csharp/chapter_hashing/array_hash_map.cs b/codes/csharp/chapter_hashing/array_hash_map.cs index 988c7e88..5692b2c6 100644 --- a/codes/csharp/chapter_hashing/array_hash_map.cs +++ b/codes/csharp/chapter_hashing/array_hash_map.cs @@ -7,10 +7,10 @@ namespace hello_algo.chapter_hashing; /* 键值对 int->string */ -class Entry { +class Pair { public int key; public string val; - public Entry(int key, string val) { + public Pair(int key, string val) { this.key = key; this.val = val; } @@ -18,7 +18,7 @@ class Entry { /* 基于数组简易实现的哈希表 */ class ArrayHashMap { - private List buckets; + private List buckets; public ArrayHashMap() { // 初始化数组,包含 100 个桶 buckets = new(); @@ -36,14 +36,14 @@ class ArrayHashMap { /* 查询操作 */ public string? get(int key) { int index = hashFunc(key); - Entry? pair = buckets[index]; + Pair? pair = buckets[index]; if (pair == null) return null; return pair.val; } /* 添加操作 */ public void put(int key, string val) { - Entry pair = new Entry(key, val); + Pair pair = new Pair(key, val); int index = hashFunc(key); buckets[index] = pair; } @@ -56,19 +56,19 @@ class ArrayHashMap { } /* 获取所有键值对 */ - public List entrySet() { - List entrySet = new(); - foreach (Entry? pair in buckets) { + public List pairSet() { + List pairSet = new(); + foreach (Pair? pair in buckets) { if (pair != null) - entrySet.Add(pair); + pairSet.Add(pair); } - return entrySet; + return pairSet; } /* 获取所有键 */ public List keySet() { List keySet = new(); - foreach (Entry? pair in buckets) { + foreach (Pair? pair in buckets) { if (pair != null) keySet.Add(pair.key); } @@ -78,7 +78,7 @@ class ArrayHashMap { /* 获取所有值 */ public List valueSet() { List valueSet = new(); - foreach (Entry? pair in buckets) { + foreach (Pair? pair in buckets) { if (pair != null) valueSet.Add(pair.val); } @@ -87,7 +87,7 @@ class ArrayHashMap { /* 打印哈希表 */ public void print() { - foreach (Entry kv in entrySet()) { + foreach (Pair kv in pairSet()) { Console.WriteLine(kv.key + " -> " + kv.val); } } @@ -123,7 +123,7 @@ public class array_hash_map { /* 遍历哈希表 */ Console.WriteLine("\n遍历键值对 Key->Value"); - foreach (Entry kv in map.entrySet()) { + foreach (Pair kv in map.pairSet()) { Console.WriteLine(kv.key + " -> " + kv.val); } Console.WriteLine("\n单独遍历键 Key"); diff --git a/codes/dart/chapter_hashing/array_hash_map.dart b/codes/dart/chapter_hashing/array_hash_map.dart index ba9512bd..945a06cd 100644 --- a/codes/dart/chapter_hashing/array_hash_map.dart +++ b/codes/dart/chapter_hashing/array_hash_map.dart @@ -4,16 +4,16 @@ * Author: liuyuxin (gvenusleo@gmail.com) */ -/* 键值对 int -> String */ -class Entry { +/* 键值对 */ +class Pair { int key; String val; - Entry(this.key, this.val); + Pair(this.key, this.val); } /* 基于数组简易实现的哈希表 */ class ArrayHashMap { - late List _buckets; + late List _buckets; ArrayHashMap() { // 初始化数组,包含 100 个桶 @@ -29,7 +29,7 @@ class ArrayHashMap { /* 查询操作 */ String? get(int key) { final int index = _hashFunc(key); - final Entry? pair = _buckets[index]; + final Pair? pair = _buckets[index]; if (pair == null) { return null; } @@ -38,7 +38,7 @@ class ArrayHashMap { /* 添加操作 */ void put(int key, String val) { - final Entry pair = Entry(key, val); + final Pair pair = Pair(key, val); final int index = _hashFunc(key); _buckets[index] = pair; } @@ -50,20 +50,20 @@ class ArrayHashMap { } /* 获取所有键值对 */ - List entrySet() { - List entrySet = []; - for (final Entry? pair in _buckets) { + List pairSet() { + List pairSet = []; + for (final Pair? pair in _buckets) { if (pair != null) { - entrySet.add(pair); + pairSet.add(pair); } } - return entrySet; + return pairSet; } /* 获取所有键 */ List keySet() { List keySet = []; - for (final Entry? pair in _buckets) { + for (final Pair? pair in _buckets) { if (pair != null) { keySet.add(pair.key); } @@ -74,7 +74,7 @@ class ArrayHashMap { /* 获取所有值 */ List values() { List valueSet = []; - for (final Entry? pair in _buckets) { + for (final Pair? pair in _buckets) { if (pair != null) { valueSet.add(pair.val); } @@ -84,7 +84,7 @@ class ArrayHashMap { /* 打印哈希表 */ void printHashMap() { - for (final Entry kv in entrySet()) { + for (final Pair kv in pairSet()) { print("${kv.key} -> ${kv.val}"); } } @@ -118,7 +118,7 @@ void main() { /* 遍历哈希表 */ print("\n遍历键值对 Key->Value"); - map.entrySet().forEach((kv) => print("${kv.key} -> ${kv.val}")); + map.pairSet().forEach((kv) => print("${kv.key} -> ${kv.val}")); print("\n单独遍历键 Key"); map.keySet().forEach((key) => print("$key")); print("\n单独遍历值 Value"); diff --git a/codes/go/chapter_hashing/array_hash_map.go b/codes/go/chapter_hashing/array_hash_map.go index 22b886e2..7ef1ccbd 100644 --- a/codes/go/chapter_hashing/array_hash_map.go +++ b/codes/go/chapter_hashing/array_hash_map.go @@ -6,21 +6,21 @@ package chapter_hashing import "fmt" -/* 键值对 int->String */ -type entry struct { +/* 键值对 */ +type pair struct { key int val string } /* 基于数组简易实现的哈希表 */ type arrayHashMap struct { - buckets []*entry + buckets []*pair } /* 初始化哈希表 */ func newArrayHashMap() *arrayHashMap { // 初始化数组,包含 100 个桶 - buckets := make([]*entry, 100) + buckets := make([]*pair, 100) return &arrayHashMap{buckets: buckets} } @@ -42,7 +42,7 @@ func (a *arrayHashMap) get(key int) string { /* 添加操作 */ func (a *arrayHashMap) put(key int, val string) { - pair := &entry{key: key, val: val} + pair := &pair{key: key, val: val} index := a.hashFunc(key) a.buckets[index] = pair } @@ -55,8 +55,8 @@ func (a *arrayHashMap) remove(key int) { } /* 获取所有键对 */ -func (a *arrayHashMap) entrySet() []*entry { - var pairs []*entry +func (a *arrayHashMap) pairSet() []*pair { + var pairs []*pair for _, pair := range a.buckets { if pair != nil { pairs = append(pairs, pair) diff --git a/codes/go/chapter_hashing/array_hash_map_test.go b/codes/go/chapter_hashing/array_hash_map_test.go index 6925f0d5..d3c7e462 100644 --- a/codes/go/chapter_hashing/array_hash_map_test.go +++ b/codes/go/chapter_hashing/array_hash_map_test.go @@ -36,7 +36,7 @@ func TestArrayHashMap(t *testing.T) { /* 遍历哈希表 */ fmt.Println("\n遍历键值对 Key->Value") - for _, kv := range mapp.entrySet() { + for _, kv := range mapp.pairSet() { fmt.Println(kv.key, " -> ", kv.val) } diff --git a/codes/java/chapter_graph/graph_adjacency_list.java b/codes/java/chapter_graph/graph_adjacency_list.java index 28e1d8f9..aaadd45b 100644 --- a/codes/java/chapter_graph/graph_adjacency_list.java +++ b/codes/java/chapter_graph/graph_adjacency_list.java @@ -71,11 +71,11 @@ class GraphAdjList { /* 打印邻接表 */ public void print() { System.out.println("邻接表 ="); - for (Map.Entry> entry : adjList.entrySet()) { + for (Map.Entry> pair : adjList.entrySet()) { List tmp = new ArrayList<>(); - for (Vertex vertex : entry.getValue()) + for (Vertex vertex : pair.getValue()) tmp.add(vertex.val); - System.out.println(entry.getKey().val + ": " + tmp + ","); + System.out.println(pair.getKey().val + ": " + tmp + ","); } } } diff --git a/codes/java/chapter_hashing/array_hash_map.java b/codes/java/chapter_hashing/array_hash_map.java index 6a09d79c..b1c410d3 100644 --- a/codes/java/chapter_hashing/array_hash_map.java +++ b/codes/java/chapter_hashing/array_hash_map.java @@ -8,12 +8,12 @@ package chapter_hashing; import java.util.*; -/* 键值对 int->String */ -class Entry { +/* 键值对 */ +class Pair { public int key; public String val; - public Entry(int key, String val) { + public Pair(int key, String val) { this.key = key; this.val = val; } @@ -21,7 +21,7 @@ class Entry { /* 基于数组简易实现的哈希表 */ class ArrayHashMap { - private List buckets; + private List buckets; public ArrayHashMap() { // 初始化数组,包含 100 个桶 @@ -40,7 +40,7 @@ class ArrayHashMap { /* 查询操作 */ public String get(int key) { int index = hashFunc(key); - Entry pair = buckets.get(index); + Pair pair = buckets.get(index); if (pair == null) return null; return pair.val; @@ -48,7 +48,7 @@ class ArrayHashMap { /* 添加操作 */ public void put(int key, String val) { - Entry pair = new Entry(key, val); + Pair pair = new Pair(key, val); int index = hashFunc(key); buckets.set(index, pair); } @@ -61,19 +61,19 @@ class ArrayHashMap { } /* 获取所有键值对 */ - public List entrySet() { - List entrySet = new ArrayList<>(); - for (Entry pair : buckets) { + public List pairSet() { + List pairSet = new ArrayList<>(); + for (Pair pair : buckets) { if (pair != null) - entrySet.add(pair); + pairSet.add(pair); } - return entrySet; + return pairSet; } /* 获取所有键 */ public List keySet() { List keySet = new ArrayList<>(); - for (Entry pair : buckets) { + for (Pair pair : buckets) { if (pair != null) keySet.add(pair.key); } @@ -83,7 +83,7 @@ class ArrayHashMap { /* 获取所有值 */ public List valueSet() { List valueSet = new ArrayList<>(); - for (Entry pair : buckets) { + for (Pair pair : buckets) { if (pair != null) valueSet.add(pair.val); } @@ -92,7 +92,7 @@ class ArrayHashMap { /* 打印哈希表 */ public void print() { - for (Entry kv : entrySet()) { + for (Pair kv : pairSet()) { System.out.println(kv.key + " -> " + kv.val); } } @@ -126,7 +126,7 @@ public class array_hash_map { /* 遍历哈希表 */ System.out.println("\n遍历键值对 Key->Value"); - for (Entry kv : map.entrySet()) { + for (Pair kv : map.pairSet()) { System.out.println(kv.key + " -> " + kv.val); } System.out.println("\n单独遍历键 Key"); diff --git a/codes/java/chapter_hashing/hash_map_chaining.java b/codes/java/chapter_hashing/hash_map_chaining.java new file mode 100644 index 00000000..ff87d1db --- /dev/null +++ b/codes/java/chapter_hashing/hash_map_chaining.java @@ -0,0 +1,157 @@ +/** + * File: hash_map_chaining.java + * Created Time: 2023-06-13 + * Author: Krahets (krahets@163.com) + */ + +package chapter_hashing; + +import java.util.ArrayList; +import java.util.List; + +/* 键值对 */ +class Pair { + public int key; + public String val; + + public Pair(int key, String val) { + this.key = key; + this.val = val; + } +} + +/* 链式地址哈希表 */ +class HashMapChaining { + int size; // 键值对数量 + int capacity; // 哈希表容量 + double loadThres; // 触发扩容的负载因子阈值 + int extendRatio; // 扩容倍数 + List> buckets; // 桶数组 + + /* 构造方法 */ + public HashMapChaining() { + size = 0; + capacity = 4; + loadThres = 2 / 3.0; + extendRatio = 2; + buckets = new ArrayList<>(capacity); + for (int i = 0; i < capacity; i++) { + buckets.add(new ArrayList<>()); + } + } + + /* 哈希函数 */ + int hashFunc(int key) { + return key % capacity; + } + + /* 负载因子 */ + double loadFactor() { + return (double) size / capacity; + } + + /* 查询操作 */ + String get(int key) { + int index = hashFunc(key); + List bucket = buckets.get(index); + // 遍历桶,若找到 key 则返回对应 val + for (Pair pair : bucket) { + if (pair.key == key) { + return pair.val; + } + } + // 若未找到 key 则返回 null + return null; + } + + /* 添加操作 */ + void put(int key, String val) { + // 当负载因子超过阈值时,执行扩容 + if (loadFactor() > loadThres) { + extend(); + } + int index = hashFunc(key); + List bucket = buckets.get(index); + // 遍历桶,若遇到指定 key ,则更新对应 val 并返回 + for (Pair pair : bucket) { + if (pair.key == key) { + pair.val = val; + return; + } + } + // 若无该 key ,则将键值对添加至尾部 + Pair pair = new Pair(key, val); + bucket.add(pair); + size++; + } + + /* 删除操作 */ + void remove(int key) { + int index = hashFunc(key); + List bucket = buckets.get(index); + // 遍历桶,从中删除键值对 + for (Pair pair : bucket) { + if (pair.key == key) + bucket.remove(pair); + } + size--; + } + + /* 扩容哈希表 */ + void extend() { + // 暂存原哈希表 + List> bucketsTmp = buckets; + // 初始化扩容后的新哈希表 + capacity *= extendRatio; + buckets = new ArrayList<>(capacity); + for (int i = 0; i < capacity; i++) { + buckets.add(new ArrayList<>()); + } + size = 0; + // 将键值对从原哈希表搬运至新哈希表 + for (List bucket : bucketsTmp) { + for (Pair pair : bucket) { + put(pair.key, pair.val); + } + } + } + + /* 打印哈希表 */ + void print() { + for (List bucket : buckets) { + List res = new ArrayList<>(); + for (Pair pair : bucket) { + res.add(pair.key + " -> " + pair.val); + } + System.out.println(res); + } + } +} + +public class hash_map_chaining { + public static void main(String[] args) { + /* 初始化哈希表 */ + HashMapChaining map = new HashMapChaining(); + + /* 添加操作 */ + // 在哈希表中添加键值对 (key, value) + map.put(12836, "小哈"); + map.put(15937, "小啰"); + map.put(16750, "小算"); + map.put(13276, "小法"); + map.put(10583, "小鸭"); + System.out.println("\n添加完成后,哈希表为\nKey -> Value"); + map.print(); + + /* 查询操作 */ + // 向哈希表输入键 key ,得到值 value + String name = map.get(13276); + System.out.println("\n输入学号 13276 ,查询到姓名 " + name); + + /* 删除操作 */ + // 在哈希表中删除键值对 (key, value) + map.remove(12836); + System.out.println("\n删除 12836 后,哈希表为\nKey -> Value"); + map.print(); + } +} diff --git a/codes/java/chapter_hashing/hash_map_open_addressing.java b/codes/java/chapter_hashing/hash_map_open_addressing.java new file mode 100644 index 00000000..2116bfb9 --- /dev/null +++ b/codes/java/chapter_hashing/hash_map_open_addressing.java @@ -0,0 +1,165 @@ +/** + * File: hash_map_chaining.java + * Created Time: 2023-06-13 + * Author: Krahets (krahets@163.com) + */ + +package chapter_hashing; + +/* 键值对 */ +class Pair { + public int key; + public String val; + + public Pair(int key, String val) { + this.key = key; + this.val = val; + } +} + +/* 开放寻址哈希表 */ +class HashMapOpenAddressing { + private int size; // 键值对数量 + private int capacity; // 哈希表容量 + private double loadThres; // 触发扩容的负载因子阈值 + private int extendRatio; // 扩容倍数 + private Pair[] buckets; // 桶数组 + private Pair removed; // 删除标记 + + /* 构造方法 */ + public HashMapOpenAddressing() { + size = 0; + capacity = 4; + loadThres = 2.0 / 3.0; + extendRatio = 2; + buckets = new Pair[capacity]; + removed = new Pair(-1, "-1"); + } + + /* 哈希函数 */ + public int hashFunc(int key) { + return key % capacity; + } + + /* 负载因子 */ + public double loadFactor() { + return (double) size / capacity; + } + + /* 查询操作 */ + public String get(int key) { + int index = hashFunc(key); + // 线性探测,从 index 开始向后遍历 + for (int i = 0; i < capacity; i++) { + // 计算桶索引,越过尾部返回头部 + int j = (index + i) % capacity; + // 若遇到空桶,说明无此 key ,则返回 null + if (buckets[j] == null) + return null; + // 若遇到指定 key ,则返回对应 val + if (buckets[j].key == key && buckets[j] != removed) + return buckets[j].val; + } + return null; + } + + /* 添加操作 */ + public void put(int key, String val) { + // 当负载因子超过阈值时,执行扩容 + if (loadFactor() > loadThres) { + extend(); + } + int index = hashFunc(key); + // 线性探测,从 index 开始向后遍历 + for (int i = 0; i < capacity; i++) { + // 计算桶索引,越过尾部返回头部 + int j = (index + i) % capacity; + // 若遇到空桶、或带有删除标记的桶,则将键值对放入该桶 + if (buckets[j] == null || buckets[j] == removed) { + buckets[j] = new Pair(key, val); + size += 1; + return; + } + // 若遇到指定 key ,则更新对应 val + if (buckets[j].key == key) { + buckets[j].val = val; + return; + } + } + } + + /* 删除操作 */ + public void remove(int key) { + int index = hashFunc(key); + // 线性探测,从 index 开始向后遍历 + for (int i = 0; i < capacity; i++) { + // 计算桶索引,越过尾部返回头部 + int j = (index + i) % capacity; + // 若遇到空桶,说明无此 key ,则直接返回 + if (buckets[j] == null) { + return; + } + // 若遇到指定 key ,则标记删除并返回 + if (buckets[j].key == key) { + buckets[j] = removed; + size -= 1; + return; + } + } + } + + /* 扩容哈希表 */ + public void extend() { + // 暂存原哈希表 + Pair[] bucketsTmp = buckets; + // 初始化扩容后的新哈希表 + capacity *= extendRatio; + buckets = new Pair[capacity]; + size = 0; + // 将键值对从原哈希表搬运至新哈希表 + for (Pair pair : bucketsTmp) { + if (pair != null && pair != removed) { + put(pair.key, pair.val); + } + } + } + + /* 打印哈希表 */ + public void print() { + for (Pair pair : buckets) { + if (pair != null) { + System.out.println(pair.key + " -> " + pair.val); + } else { + System.out.println("null"); + } + } + } +} + +public class hash_map_open_addressing { + public static void main(String[] args) { + /* 初始化哈希表 */ + HashMapOpenAddressing map = new HashMapOpenAddressing(); + + /* 添加操作 */ + // 在哈希表中添加键值对 (key, value) + map.put(12836, "小哈"); + map.put(15937, "小啰"); + map.put(16750, "小算"); + map.put(13276, "小法"); + map.put(10583, "小鸭"); + System.out.println("\n添加完成后,哈希表为\nKey -> Value"); + map.print(); + + /* 查询操作 */ + // 向哈希表输入键 key ,得到值 value + String name = map.get(13276); + System.out.println("\n输入学号 13276 ,查询到姓名 " + name); + + /* 删除操作 */ + // 在哈希表中删除键值对 (key, value) + map.remove(16750); + System.out.println("\n删除 16750 后,哈希表为\nKey -> Value"); + map.print(); + } +} diff --git a/codes/javascript/chapter_hashing/array_hash_map.js b/codes/javascript/chapter_hashing/array_hash_map.js index 7e161159..270825e8 100644 --- a/codes/javascript/chapter_hashing/array_hash_map.js +++ b/codes/javascript/chapter_hashing/array_hash_map.js @@ -5,7 +5,7 @@ */ /* 键值对 Number -> String */ -class Entry { +class Pair { constructor(key, val) { this.key = key; this.val = val; @@ -28,15 +28,15 @@ class ArrayHashMap { /* 查询操作 */ get(key) { let index = this.#hashFunc(key); - let entry = this.#buckets[index]; - if (entry === null) return null; - return entry.val; + let pair = this.#buckets[index]; + if (pair === null) return null; + return pair.val; } /* 添加操作 */ set(key, val) { let index = this.#hashFunc(key); - this.#buckets[index] = new Entry(key, val); + this.#buckets[index] = new Pair(key, val); } /* 删除操作 */ @@ -81,10 +81,10 @@ class ArrayHashMap { /* 打印哈希表 */ print() { - let entrySet = this.entries(); - for (const entry of entrySet) { - if (!entry) continue; - console.info(`${entry.key} -> ${entry.val}`); + let pairSet = this.entries(); + for (const pair of pairSet) { + if (!pair) continue; + console.info(`${pair.key} -> ${pair.val}`); } } } @@ -115,9 +115,9 @@ map.print(); /* 遍历哈希表 */ console.info('\n遍历键值对 Key->Value'); -for (const entry of map.entries()) { - if (!entry) continue; - console.info(entry.key + ' -> ' + entry.val); +for (const pair of map.entries()) { + if (!pair) continue; + console.info(pair.key + ' -> ' + pair.val); } console.info('\n单独遍历键 Key'); for (const key of map.keys()) { diff --git a/codes/python/chapter_hashing/array_hash_map.py b/codes/python/chapter_hashing/array_hash_map.py index 84e15062..e2f9ddbb 100644 --- a/codes/python/chapter_hashing/array_hash_map.py +++ b/codes/python/chapter_hashing/array_hash_map.py @@ -5,8 +5,8 @@ Author: msk397 (machangxinq@gmail.com) """ -class Entry: - """键值对 int->String""" +class Pair: + """键值对""" def __init__(self, key: int, val: str): self.key = key @@ -19,7 +19,7 @@ class ArrayHashMap: def __init__(self): """构造方法""" # 初始化数组,包含 100 个桶 - self.buckets: list[Entry | None] = [None] * 100 + self.buckets: list[Pair | None] = [None] * 100 def hash_func(self, key: int) -> int: """哈希函数""" @@ -29,26 +29,26 @@ class ArrayHashMap: def get(self, key: int) -> str: """查询操作""" index: int = self.hash_func(key) - pair: Entry = self.buckets[index] + pair: Pair = self.buckets[index] if pair is None: return None return pair.val - def put(self, key: int, val: str) -> None: + def put(self, key: int, val: str): """添加操作""" - pair = Entry(key, val) + pair = Pair(key, val) index: int = self.hash_func(key) self.buckets[index] = pair - def remove(self, key: int) -> None: + def remove(self, key: int): """删除操作""" index: int = self.hash_func(key) # 置为 None ,代表删除 self.buckets[index] = None - def entry_set(self) -> list[Entry]: + def entry_set(self) -> list[Pair]: """获取所有键值对""" - result: list[Entry] = [] + result: list[Pair] = [] for pair in self.buckets: if pair is not None: result.append(pair) @@ -70,7 +70,7 @@ class ArrayHashMap: result.append(pair.val) return result - def print(self) -> None: + def print(self): """打印哈希表""" for pair in self.buckets: if pair is not None: diff --git a/codes/python/chapter_hashing/hash_map_chaining.py b/codes/python/chapter_hashing/hash_map_chaining.py new file mode 100644 index 00000000..dfe1ccae --- /dev/null +++ b/codes/python/chapter_hashing/hash_map_chaining.py @@ -0,0 +1,119 @@ +""" +File: hash_map_chaining.py +Created Time: 2023-06-13 +Author: Krahets (krahets@163.com) +""" + +class Pair: + """键值对""" + + def __init__(self, key: int, val: str): + self.key = key + self.val = val + + +class HashMapChaining: + """链式地址哈希表""" + + def __init__(self): + """构造方法""" + self.size = 0 # 键值对数量 + self.capacity = 4 # 哈希表容量 + self.load_thres = 2 / 3 # 触发扩容的负载因子阈值 + self.extend_ratio = 2 # 扩容倍数 + self.buckets = [[] for _ in range(self.capacity)] # 桶数组 + + def hash_func(self, key: int) -> int: + """哈希函数""" + return key % self.capacity + + def load_factor(self) -> float: + """负载因子""" + return self.size / self.capacity + + def get(self, key: int) -> str: + """查询操作""" + index = self.hash_func(key) + bucket = self.buckets[index] + # 遍历桶,若找到 key 则返回对应 val + for pair in bucket: + if pair.key == key: + return pair.val + # 若未找到 key 则返回 None + return None + + def put(self, key: int, val: str): + """添加操作""" + # 当负载因子超过阈值时,执行扩容 + if self.load_factor() > self.load_thres: + self.extend() + index = self.hash_func(key) + bucket = self.buckets[index] + # 遍历桶,若遇到指定 key ,则更新对应 val 并返回 + for pair in bucket: + if pair.key == key: + pair.val = val + return + # 若无该 key ,则将键值对添加至尾部 + pair = Pair(key, val) + bucket.append(pair) + self.size += 1 + + def remove(self, key: int): + """删除操作""" + index = self.hash_func(key) + bucket = self.buckets[index] + # 遍历桶,从中删除键值对 + for pair in bucket: + if pair.key == key: + bucket.remove(pair) + self.size -= 1 + return + + def extend(self): + """扩容哈希表""" + # 暂存原哈希表 + buckets = self.buckets + # 初始化扩容后的新哈希表 + self.capacity *= self.extend_ratio + self.buckets = [[] for _ in range(self.capacity)] + self.size = 0 + # 将键值对从原哈希表搬运至新哈希表 + for bucket in buckets: + for pair in bucket: + self.put(pair.key, pair.val) + + def print(self): + """打印哈希表""" + for bucket in self.buckets: + res = [] + for pair in bucket: + res.append(str(pair.key) + " -> " + pair.val) + print(res) + + +"""Driver Code""" +if __name__ == "__main__": + # 测试代码 + hashmap = HashMapChaining() + + # 添加操作 + # 在哈希表中添加键值对 (key, value) + hashmap.put(12836, "小哈") + hashmap.put(15937, "小啰") + hashmap.put(16750, "小算") + hashmap.put(13276, "小法") + hashmap.put(10583, "小鸭") + print("\n添加完成后,哈希表为\n[Key1 -> Value1, Key2 -> Value2, ...]") + hashmap.print() + + # 查询操作 + # 向哈希表输入键 key ,得到值 value + name = hashmap.get(13276) + print("\n输入学号 13276 ,查询到姓名 " + name) + + # 删除操作 + # 在哈希表中删除键值对 (key, value) + hashmap.remove(12836) + print("\n删除 12836 后,哈希表为\n[Key1 -> Value1, Key2 -> Value2, ...]") + hashmap.print() diff --git a/codes/python/chapter_hashing/hash_map_open_addressing.py b/codes/python/chapter_hashing/hash_map_open_addressing.py new file mode 100644 index 00000000..64a2a233 --- /dev/null +++ b/codes/python/chapter_hashing/hash_map_open_addressing.py @@ -0,0 +1,132 @@ +""" +File: hash_map_open_addressing.py +Created Time: 2023-06-13 +Author: Krahets (krahets@163.com) +""" + + +class Pair: + """键值对""" + + def __init__(self, key: int, val: str): + self.key = key + self.val = val + + +class HashMapOpenAddressing: + """开放寻址哈希表""" + + def __init__(self): + """构造方法""" + self.size = 0 # 键值对数量 + self.capacity = 4 # 哈希表容量 + self.load_thres = 2 / 3 # 触发扩容的负载因子阈值 + self.extend_ratio = 2 # 扩容倍数 + self.buckets: list[Pair | None] = [None] * self.capacity # 桶数组 + self.removed = Pair(-1, "-1") # 删除标记 + + def hash_func(self, key: int) -> int: + """哈希函数""" + return key % self.capacity + + def load_factor(self) -> float: + """负载因子""" + return self.size / self.capacity + + def get(self, key: int) -> str: + """查询操作""" + index = self.hash_func(key) + # 线性探测,从 index 开始向后遍历 + for i in range(self.capacity): + # 计算桶索引,越过尾部返回头部 + j = (index + i) % self.capacity + # 若遇到空桶,说明无此 key ,则返回 None + if self.buckets[j] is None: + return None + # 若遇到指定 key ,则返回对应 val + if self.buckets[j].key == key and self.buckets[j] != self.removed: + return self.buckets[j].val + + def put(self, key: int, val: str): + """添加操作""" + # 当负载因子超过阈值时,执行扩容 + if self.load_factor() > self.load_thres: + self.extend() + index = self.hash_func(key) + # 线性探测,从 index 开始向后遍历 + for i in range(self.capacity): + # 计算桶索引,越过尾部返回头部 + j = (index + i) % self.capacity + # 若遇到空桶、或带有删除标记的桶,则将键值对放入该桶 + if self.buckets[j] in [None, self.removed]: + self.buckets[j] = Pair(key, val) + self.size += 1 + return + # 若遇到指定 key ,则更新对应 val + if self.buckets[j].key == key: + self.buckets[j].val = val + return + + def remove(self, key: int): + """删除操作""" + index = self.hash_func(key) + # 线性探测,从 index 开始向后遍历 + for i in range(self.capacity): + # 计算桶索引,越过尾部返回头部 + j = (index + i) % self.capacity + # 若遇到空桶,说明无此 key ,则直接返回 + if self.buckets[j] is None: + return + # 若遇到指定 key ,则标记删除并返回 + if self.buckets[j].key == key: + self.buckets[j] = self.removed + self.size -= 1 + return + + def extend(self): + """扩容哈希表""" + # 暂存原哈希表 + buckets_tmp = self.buckets + # 初始化扩容后的新哈希表 + self.capacity *= self.extend_ratio + self.buckets = [None] * self.capacity + self.size = 0 + # 将键值对从原哈希表搬运至新哈希表 + for pair in buckets_tmp: + if pair not in [None, self.removed]: + self.put(pair.key, pair.val) + + def print(self) -> None: + """打印哈希表""" + for pair in self.buckets: + if pair is not None: + print(pair.key, "->", pair.val) + else: + print("None") + + +"""Driver Code""" +if __name__ == "__main__": + # 测试代码 + hashmap = HashMapOpenAddressing() + + # 添加操作 + # 在哈希表中添加键值对 (key, val) + hashmap.put(12836, "小哈") + hashmap.put(15937, "小啰") + hashmap.put(16750, "小算") + hashmap.put(13276, "小法") + hashmap.put(10583, "小鸭") + print("\n添加完成后,哈希表为\nKey -> Value") + hashmap.print() + + # 查询操作 + # 向哈希表输入键 key ,得到值 val + name = hashmap.get(13276) + print("\n输入学号 13276 ,查询到姓名 " + name) + + # 删除操作 + # 在哈希表中删除键值对 (key, val) + hashmap.remove(16750) + print("\n删除 16750 后,哈希表为\nKey -> Value") + hashmap.print() diff --git a/codes/rust/chapter_hashing/array_hash_map.rs b/codes/rust/chapter_hashing/array_hash_map.rs index 59857fce..b5ef29c1 100644 --- a/codes/rust/chapter_hashing/array_hash_map.rs +++ b/codes/rust/chapter_hashing/array_hash_map.rs @@ -5,14 +5,14 @@ */ #[derive(Debug, Clone)] -/* 键值对 int->String */ -pub struct Entry { +/* 键值对 */ +pub struct Pair { pub key: i32, pub val: String, } /* 基于数组简易实现的哈希表 */ -pub struct ArrayHashMap { buckets: Vec> } +pub struct ArrayHashMap { buckets: Vec> } impl ArrayHashMap { pub fn new() -> ArrayHashMap { @@ -28,13 +28,13 @@ impl ArrayHashMap { /* 查询操作 */ pub fn get(&self, key: i32) -> Option<&String> { let index = self.hash_func(key); - self.buckets[index].as_ref().map(|entry| &entry.val) + self.buckets[index].as_ref().map(|pair| &pair.val) } /* 添加操作 */ pub fn put(&mut self, key: i32, val: &str) { let index = self.hash_func(key); - self.buckets[index] = Some(Entry { + self.buckets[index] = Some(Pair { key, val: val.to_string(), }); @@ -47,24 +47,24 @@ impl ArrayHashMap { } /* 获取所有键值对 */ - pub fn entry_set(&self) -> Vec<&Entry> { - self.buckets.iter().filter_map(|entry| entry.as_ref()).collect() + pub fn entry_set(&self) -> Vec<&Pair> { + self.buckets.iter().filter_map(|pair| pair.as_ref()).collect() } /* 获取所有键 */ pub fn key_set(&self) -> Vec<&i32> { - self.buckets.iter().filter_map(|entry| entry.as_ref().map(|entry| &entry.key)).collect() + self.buckets.iter().filter_map(|pair| pair.as_ref().map(|pair| &pair.key)).collect() } /* 获取所有值 */ pub fn value_set(&self) -> Vec<&String> { - self.buckets.iter().filter_map(|entry| entry.as_ref().map(|entry| &entry.val)).collect() + self.buckets.iter().filter_map(|pair| pair.as_ref().map(|pair| &pair.val)).collect() } /* 打印哈希表 */ pub fn print(&self) { - for entry in self.entry_set() { - println!("{} -> {}", entry.key, entry.val); + for pair in self.entry_set() { + println!("{} -> {}", pair.key, pair.val); } } } @@ -95,8 +95,8 @@ fn main() { /* 遍历哈希表 */ println!("\n遍历键值对 Key->Value"); - for entry in map.entry_set() { - println!("{} -> {}", entry.key, entry.val); + for pair in map.entry_set() { + println!("{} -> {}", pair.key, pair.val); } println!("\n单独遍历键 Key"); diff --git a/codes/swift/chapter_graph/graph_adjacency_list.swift b/codes/swift/chapter_graph/graph_adjacency_list.swift index 637efb08..dac9869e 100644 --- a/codes/swift/chapter_graph/graph_adjacency_list.swift +++ b/codes/swift/chapter_graph/graph_adjacency_list.swift @@ -72,12 +72,12 @@ public class GraphAdjList { /* 打印邻接表 */ public func print() { Swift.print("邻接表 =") - for entry in adjList { + for pair in adjList { var tmp: [Int] = [] - for vertex in entry.value { + for vertex in pair.value { tmp.append(vertex.val) } - Swift.print("\(entry.key.val): \(tmp),") + Swift.print("\(pair.key.val): \(tmp),") } } } diff --git a/codes/swift/chapter_hashing/array_hash_map.swift b/codes/swift/chapter_hashing/array_hash_map.swift index fc943803..54d78da0 100644 --- a/codes/swift/chapter_hashing/array_hash_map.swift +++ b/codes/swift/chapter_hashing/array_hash_map.swift @@ -4,8 +4,8 @@ * Author: nuomi1 (nuomi1@qq.com) */ -/* 键值对 int->String */ -class Entry { +/* 键值对 */ +class Pair { var key: Int var val: String @@ -17,7 +17,7 @@ class Entry { /* 基于数组简易实现的哈希表 */ class ArrayHashMap { - private var buckets: [Entry?] = [] + private var buckets: [Pair?] = [] init() { // 初始化数组,包含 100 个桶 @@ -41,7 +41,7 @@ class ArrayHashMap { /* 添加操作 */ func put(key: Int, val: String) { - let pair = Entry(key: key, val: val) + let pair = Pair(key: key, val: val) let index = hashFunc(key: key) buckets[index] = pair } @@ -54,14 +54,14 @@ class ArrayHashMap { } /* 获取所有键值对 */ - func entrySet() -> [Entry] { - var entrySet: [Entry] = [] + func pairSet() -> [Pair] { + var pairSet: [Pair] = [] for pair in buckets { if let pair = pair { - entrySet.append(pair) + pairSet.append(pair) } } - return entrySet + return pairSet } /* 获取所有键 */ @@ -88,8 +88,8 @@ class ArrayHashMap { /* 打印哈希表 */ func print() { - for entry in entrySet() { - Swift.print("\(entry.key) -> \(entry.val)") + for pair in pairSet() { + Swift.print("\(pair.key) -> \(pair.val)") } } } @@ -124,8 +124,8 @@ enum _ArrayHashMap { /* 遍历哈希表 */ print("\n遍历键值对 Key->Value") - for entry in map.entrySet() { - print("\(entry.key) -> \(entry.val)") + for pair in map.pairSet() { + print("\(pair.key) -> \(pair.val)") } print("\n单独遍历键 Key") for key in map.keySet() { diff --git a/codes/typescript/chapter_hashing/array_hash_map.ts b/codes/typescript/chapter_hashing/array_hash_map.ts index e0a92a6a..a039ea93 100644 --- a/codes/typescript/chapter_hashing/array_hash_map.ts +++ b/codes/typescript/chapter_hashing/array_hash_map.ts @@ -5,7 +5,7 @@ */ /* 键值对 Number -> String */ -class Entry { +class Pair { public key: number; public val: string; @@ -17,7 +17,7 @@ class Entry { /* 基于数组简易实现的哈希表 */ class ArrayHashMap { - private readonly buckets: (Entry | null)[]; + private readonly buckets: (Pair | null)[]; constructor() { // 初始化数组,包含 100 个桶 @@ -32,15 +32,15 @@ class ArrayHashMap { /* 查询操作 */ public get(key: number): string | null { let index = this.hashFunc(key); - let entry = this.buckets[index]; - if (entry === null) return null; - return entry.val; + let pair = this.buckets[index]; + if (pair === null) return null; + return pair.val; } /* 添加操作 */ public set(key: number, val: string) { let index = this.hashFunc(key); - this.buckets[index] = new Entry(key, val); + this.buckets[index] = new Pair(key, val); } /* 删除操作 */ @@ -51,8 +51,8 @@ class ArrayHashMap { } /* 获取所有键值对 */ - public entries(): (Entry | null)[] { - let arr: (Entry | null)[] = []; + public entries(): (Pair | null)[] { + let arr: (Pair | null)[] = []; for (let i = 0; i < this.buckets.length; i++) { if (this.buckets[i]) { arr.push(this.buckets[i]); @@ -85,10 +85,10 @@ class ArrayHashMap { /* 打印哈希表 */ public print() { - let entrySet = this.entries(); - for (const entry of entrySet) { - if (!entry) continue; - console.info(`${entry.key} -> ${entry.val}`); + let pairSet = this.entries(); + for (const pair of pairSet) { + if (!pair) continue; + console.info(`${pair.key} -> ${pair.val}`); } } } @@ -119,9 +119,9 @@ map.print(); /* 遍历哈希表 */ console.info('\n遍历键值对 Key->Value'); -for (const entry of map.entries()) { - if (!entry) continue; - console.info(entry.key + ' -> ' + entry.val); +for (const pair of map.entries()) { + if (!pair) continue; + console.info(pair.key + ' -> ' + pair.val); } console.info('\n单独遍历键 Key'); for (const key of map.keys()) { diff --git a/codes/zig/chapter_hashing/array_hash_map.zig b/codes/zig/chapter_hashing/array_hash_map.zig index cbab80af..e1444c33 100644 --- a/codes/zig/chapter_hashing/array_hash_map.zig +++ b/codes/zig/chapter_hashing/array_hash_map.zig @@ -5,13 +5,13 @@ const std = @import("std"); const inc = @import("include"); -// 键值对 int->String -const Entry = struct { +// 键值对 +const Pair = struct { key: usize = undefined, val: []const u8 = undefined, - pub fn init(key: usize, val: []const u8) Entry { - return Entry { + pub fn init(key: usize, val: []const u8) Pair { + return Pair { .key = key, .val = val, }; @@ -57,7 +57,7 @@ pub fn ArrayHashMap(comptime T: type) type { // 添加操作 pub fn put(self: *Self, key: usize, val: []const u8) !void { - var pair = Entry.init(key, val); + var pair = Pair.init(key, val); var index = hashFunc(key); self.buckets.?.items[index] = pair; } @@ -70,7 +70,7 @@ pub fn ArrayHashMap(comptime T: type) type { } // 获取所有键值对 - pub fn entrySet(self: *Self) !*std.ArrayList(T) { + pub fn pairSet(self: *Self) !*std.ArrayList(T) { var entry_set = std.ArrayList(T).init(self.mem_allocator); for (self.buckets.?.items) |item| { if (item == null) continue; @@ -101,7 +101,7 @@ pub fn ArrayHashMap(comptime T: type) type { // 打印哈希表 pub fn print(self: *Self) !void { - var entry_set = try self.entrySet(); + var entry_set = try self.pairSet(); defer entry_set.deinit(); for (entry_set.items) |item| { std.debug.print("{} -> {s}\n", .{item.key, item.val}); @@ -113,7 +113,7 @@ pub fn ArrayHashMap(comptime T: type) type { // Driver Code pub fn main() !void { // 初始化哈希表 - var map = ArrayHashMap(Entry){}; + var map = ArrayHashMap(Pair){}; try map.init(std.heap.page_allocator); defer map.deinit(); @@ -140,7 +140,7 @@ pub fn main() !void { // 遍历哈希表 std.debug.print("\n遍历键值对 Key->Value\n", .{}); - var entry_set = try map.entrySet(); + var entry_set = try map.pairSet(); for (entry_set.items) |kv| { std.debug.print("{} -> {s}\n", .{kv.key, kv.val}); } diff --git a/docs/chapter_hashing/hash_collision.md b/docs/chapter_hashing/hash_collision.md index 12600581..e4546a29 100644 --- a/docs/chapter_hashing/hash_collision.md +++ b/docs/chapter_hashing/hash_collision.md @@ -14,13 +14,13 @@ 因此,**当哈希表内的冲突总体较为严重时,编程语言通常通过扩容哈希表来缓解冲突**。类似于数组扩容,哈希表扩容需将所有键值对从原哈希表迁移至新哈希表,开销较大。 -编程语言通常使用「负载因子 Load Factor」来衡量哈希冲突的严重程度,**定义为哈希表中元素数量除以桶数量**,常作为哈希表扩容的触发条件。在 Java 中,当负载因子 $> 0.75$ 时,系统会将 HashMap 容量扩展为原先的 $2$ 倍。 +编程语言通常使用「负载因子 Load Factor」来衡量哈希冲突的严重程度,**定义为哈希表中元素数量除以桶数量**,常作为哈希表扩容的触发条件。在 Java 中,当负载因子超过 $ 0.75$ 时,系统会将 HashMap 容量扩展为原先的 $2$ 倍。 ## 链式地址 在原始哈希表中,每个桶仅能存储一个键值对。**链式地址将单个元素转换为链表,将键值对作为链表节点,将所有发生冲突的键值对都存储在同一链表中**。 -![链式地址](hash_collision.assets/hash_collision_chaining.png) +![链式地址哈希表](hash_collision.assets/hash_collision_chaining.png) 链式地址下,哈希表的操作方法包括: @@ -33,44 +33,225 @@ - **占用空间增大**,由于链表或二叉树包含节点指针,相比数组更加耗费内存空间; - **查询效率降低**,因为需要线性遍历链表来查找对应元素; -为了提高操作效率,**可以将链表转换为「AVL 树」或「红黑树」**,将查询操作的时间复杂度优化至 $O(\log n)$ 。 +以下给出了链式地址哈希表的简单实现,需要注意: + +- 为了使得代码尽量简短,我们使用列表(动态数组)代替链表。换句话说,哈希表(数组)包含多个桶,每个桶都是一个列表。 +- 以下代码实现了哈希表扩容方法。具体来看,当负载因子超过 $0.75$ 时,我们将哈希表扩容至 $2$ 倍。 + +=== "Java" + + ```java title="hash_map_chaining.java" + [class]{Pair}-[func]{} + + [class]{HashMapChaining}-[func]{} + ``` + +=== "C++" + + ```cpp title="hash_map_chaining.cpp" + [class]{Pair}-[func]{} + + [class]{HashMapChaining}-[func]{} + ``` + +=== "Python" + + ```python title="hash_map_chaining.py" + [class]{Pair}-[func]{} + + [class]{HashMapChaining}-[func]{} + ``` + +=== "Go" + + ```go title="hash_map_chaining.go" + [class]{pair}-[func]{} + + [class]{hashMapChaining}-[func]{} + ``` + +=== "JavaScript" + + ```javascript title="hash_map_chaining.js" + [class]{Pair}-[func]{} + + [class]{HashMapChaining}-[func]{} + ``` + +=== "TypeScript" + + ```typescript title="hash_map_chaining.ts" + [class]{Pair}-[func]{} + + [class]{HashMapChaining}-[func]{} + ``` + +=== "C" + + ```c title="hash_map_chaining.c" + [class]{pair}-[func]{} + + [class]{hashMapChaining}-[func]{} + ``` + +=== "C#" + + ```csharp title="hash_map_chaining.cs" + [class]{Pair}-[func]{} + + [class]{HashMapChaining}-[func]{} + ``` + +=== "Swift" + + ```swift title="hash_map_chaining.swift" + [class]{Pair}-[func]{} + + [class]{HashMapChaining}-[func]{} + ``` + +=== "Zig" + + ```zig title="hash_map_chaining.zig" + [class]{Pair}-[func]{} + + [class]{HashMapChaining}-[func]{} + ``` + +=== "Dart" + + ```dart title="hash_map_chaining.dart" + [class]{Pair}-[func]{} + + [class]{HashMapChaining}-[func]{} + ``` + +!!! tip + + 为了提高效率,**我们可以将链表转换为「AVL 树」或「红黑树」**,从而将查询操作的时间复杂度优化至 $O(\log n)$ 。 ## 开放寻址 -「开放寻址」方法不引入额外的数据结构,而是通过“多次探测”来解决哈希冲突,**探测方式主要包括线性探测、平方探测、多次哈希**。 +开放寻址法不引入额外的数据结构,而是通过“多次探测”来解决哈希冲突,**探测方式主要包括线性探测、平方探测、多次哈希**。 ### 线性探测 -「线性探测」采用固定步长的线性查找来解决哈希冲突。 +线性探测采用固定步长的线性查找来解决哈希冲突。 -**插入元素**:若出现哈希冲突,则从冲突位置向后线性遍历(步长通常为 $1$ ),直至找到空位,将元素插入其中。 - -**查找元素**:在出现哈希冲突时,使用相同步长进行线性查找,可能遇到以下两种情况。 - -1. 找到对应元素,返回 value 即可; -2. 若遇到空位,说明目标键值对不在哈希表中; +- **插入元素**:通过哈希函数计算数组索引,若发现桶内已有元素,则从冲突位置向后线性遍历(步长通常为 $1$ ),直至找到空位,将元素插入其中。 +- **查找元素**:若发现哈希冲突,则使用相同步长向后线性遍历,直到找到对应元素,返回 value 即可;或者若遇到空位,说明目标键值对不在哈希表中,返回 $\text{None}$ 。 ![线性探测](hash_collision.assets/hash_collision_linear_probing.png) -线性探测存在以下缺陷: +然而,线性探测存在以下缺陷: -- **不能直接删除元素**。删除元素会在数组内产生一个空位,查找其他元素时,该空位可能导致程序误判元素不存在(即上述第 `2.` 种情况)。因此,需要借助一个标志位来标记已删除元素。 +- **不能直接删除元素**。删除元素会在数组内产生一个空位,查找其他元素时,该空位可能导致程序误判元素不存在。因此,需要借助一个标志位来标记已删除元素。 - **容易产生聚集**。数组内连续被占用位置越长,这些连续位置发生哈希冲突的可能性越大,进一步促使这一位置的“聚堆生长”,最终导致增删查改操作效率降低。 +如以下代码所示,为开放寻址(线性探测)哈希表的简单实现,重点包括: + +- 我们使用一个固定的键值对实例 `removed` 来标记已删除元素。也就是说,当一个桶为 $\text{None}$ 或 `removed` 时,这个桶都是空的,可用于放置键值对。 +- 被标记为已删除的空间是可以再次被使用的。当插入元素时,若通过哈希函数找到了被标记为已删除的索引,则可将该元素放置到该索引。 +- 在线性探测时,我们从当前索引 `index` 向后遍历;而当越过数组尾部时,需要回到头部继续遍历。 + +=== "Java" + + ```java title="hash_map_open_addressing.java" + [class]{Pair}-[func]{} + + [class]{HashMapOpenAddressing}-[func]{} + ``` + +=== "C++" + + ```cpp title="hash_map_open_addressing.cpp" + [class]{Pair}-[func]{} + + [class]{HashMapOpenAddressing}-[func]{} + ``` + +=== "Python" + + ```python title="hash_map_open_addressing.py" + [class]{Pair}-[func]{} + + [class]{HashMapOpenAddressing}-[func]{} + ``` + +=== "Go" + + ```go title="hash_map_open_addressing.go" + [class]{pair}-[func]{} + + [class]{hashMapOpenAddressing}-[func]{} + ``` + +=== "JavaScript" + + ```javascript title="hash_map_open_addressing.js" + [class]{Pair}-[func]{} + + [class]{HashMapOpenAddressing}-[func]{} + ``` + +=== "TypeScript" + + ```typescript title="hash_map_open_addressing.ts" + [class]{Pair}-[func]{} + + [class]{HashMapOpenAddressing}-[func]{} + ``` + +=== "C" + + ```c title="hash_map_open_addressing.c" + [class]{pair}-[func]{} + + [class]{hashMapOpenAddressing}-[func]{} + ``` + +=== "C#" + + ```csharp title="hash_map_open_addressing.cs" + [class]{Pair}-[func]{} + + [class]{HashMapOpenAddressing}-[func]{} + ``` + +=== "Swift" + + ```swift title="hash_map_open_addressing.swift" + [class]{Pair}-[func]{} + + [class]{HashMapOpenAddressing}-[func]{} + ``` + +=== "Zig" + + ```zig title="hash_map_open_addressing.zig" + [class]{Pair}-[func]{} + + [class]{HashMapOpenAddressing}-[func]{} + ``` + +=== "Dart" + + ```dart title="hash_map_open_addressing.dart" + [class]{Pair}-[func]{} + + [class]{HashMapOpenAddressing}-[func]{} + ``` + ### 多次哈希 -顾名思义,「多次哈希」方法是使用多个哈希函数 $f_1(x)$ , $f_2(x)$ , $f_3(x)$ , $\cdots$ 进行探测。 +顾名思义,多次哈希方法是使用多个哈希函数 $f_1(x)$ , $f_2(x)$ , $f_3(x)$ , $\cdots$ 进行探测。 -**插入元素**:若哈希函数 $f_1(x)$ 出现冲突,则尝试 $f_2(x)$ ,以此类推,直到找到空位后插入元素。 - -**查找元素**:在相同的哈希函数顺序下进行查找,存在以下两种情况: - -1. 如果找到目标元素,则返回之; -2. 若遇到空位或已尝试所有哈希函数,则说明哈希表中不存在该元素; +- **插入元素**:若哈希函数 $f_1(x)$ 出现冲突,则尝试 $f_2(x)$ ,以此类推,直到找到空位后插入元素。 +- **查找元素**:在相同的哈希函数顺序下进行查找,直到找到目标元素时返回;或遇到空位或已尝试所有哈希函数,说明哈希表中不存在该元素,则返回 $\text{None}$ 。 与线性探测相比,多次哈希方法不易产生聚集,但多个哈希函数会增加额外的计算量。 -!!! note "哈希表设计方案" +!!! note "编程语言的选择" Java 采用「链式地址」。自 JDK 1.8 以来,当 HashMap 内数组长度达到 64 且链表长度达到 8 时,链表会被转换为红黑树以提升查找性能。 diff --git a/docs/chapter_hashing/hash_map.md b/docs/chapter_hashing/hash_map.md index 16771f86..d2dc3e3c 100755 --- a/docs/chapter_hashing/hash_map.md +++ b/docs/chapter_hashing/hash_map.md @@ -6,16 +6,20 @@ ![哈希表的抽象表示](hash_map.assets/hash_map.png) -除哈希表外,我们还可以使用数组或链表实现查询功能,各项操作的时间复杂度如下表所示。 +除哈希表外,我们还可以使用数组或链表实现元素查询,其中: -在哈希表中增删查改的时间复杂度都是 $O(1)$ ,全面胜出!因此,哈希表常用于对查找效率要求较高的场景。 +- 查询元素需要遍历所有元素,使用 $O(n)$ 时间; +- 添加元素仅需添加至尾部即可,使用 $O(1)$ 时间; +- 删除元素需要先查询再删除,使用 $O(n)$ 时间; + +然而,在哈希表中进行增删查的时间复杂度都是 $O(1)$ 。哈希表全面胜出!因此,哈希表常用于对查找效率要求较高的场景。
| | 数组 | 链表 | 哈希表 | | -------- | ------ | ------ | ------ | | 查找元素 | $O(n)$ | $O(n)$ | $O(1)$ | -| 插入元素 | $O(1)$ | $O(1)$ | $O(1)$ | +| 添加元素 | $O(1)$ | $O(1)$ | $O(1)$ | | 删除元素 | $O(n)$ | $O(n)$ | $O(1)$ |
@@ -430,12 +434,12 @@ 首先考虑最简单的情况,**仅使用一个数组来实现哈希表**。通常,我们将数组中的每个空位称为「桶 Bucket」,用于存储键值对。 -我们将键值对 key, value 封装成一个类 `Entry` ,并将所有 `Entry` 放入数组中。这样,数组中的每个 `Entry` 都具有唯一的索引。为了建立 key 和索引之间的映射关系,我们需要使用「哈希函数 Hash Function」。 +我们将键值对 key, value 封装成一个类 `Pair` ,并将所有 `Pair` 放入数组中。这样,数组中的每个 `Pair` 都具有唯一的索引。为了建立 key 和索引之间的映射关系,我们需要使用「哈希函数 Hash Function」。 设哈希表的数组为 `buckets` ,哈希函数为 `f(x)` ,那么查询操作的步骤如下: 1. 输入 `key` ,通过哈希函数计算出索引 `index` ,即 `index = f(key)` ; -2. 通过索引在数组中访问到键值对 `entry` ,即 `entry = buckets[index]` ,然后从 `entry` 中获取对应的 `value` ; +2. 通过索引在数组中访问到键值对 `pair` ,即 `pair = buckets[index]` ,然后从 `pair` 中获取对应的 `value` ; 以学生数据 `key 学号 -> value 姓名` 为例,我们可以设计如下哈希函数: @@ -450,7 +454,7 @@ $$ === "Java" ```java title="array_hash_map.java" - [class]{Entry}-[func]{} + [class]{Pair}-[func]{} [class]{ArrayHashMap}-[func]{} ``` @@ -458,7 +462,7 @@ $$ === "C++" ```cpp title="array_hash_map.cpp" - [class]{Entry}-[func]{} + [class]{Pair}-[func]{} [class]{ArrayHashMap}-[func]{} ``` @@ -466,7 +470,7 @@ $$ === "Python" ```python title="array_hash_map.py" - [class]{Entry}-[func]{} + [class]{Pair}-[func]{} [class]{ArrayHashMap}-[func]{} ``` @@ -474,7 +478,7 @@ $$ === "Go" ```go title="array_hash_map.go" - [class]{entry}-[func]{} + [class]{pair}-[func]{} [class]{arrayHashMap}-[func]{} ``` @@ -482,7 +486,7 @@ $$ === "JavaScript" ```javascript title="array_hash_map.js" - [class]{Entry}-[func]{} + [class]{Pair}-[func]{} [class]{ArrayHashMap}-[func]{} ``` @@ -490,7 +494,7 @@ $$ === "TypeScript" ```typescript title="array_hash_map.ts" - [class]{Entry}-[func]{} + [class]{Pair}-[func]{} [class]{ArrayHashMap}-[func]{} ``` @@ -498,7 +502,7 @@ $$ === "C" ```c title="array_hash_map.c" - [class]{entry}-[func]{} + [class]{pair}-[func]{} [class]{arrayHashMap}-[func]{} ``` @@ -506,7 +510,7 @@ $$ === "C#" ```csharp title="array_hash_map.cs" - [class]{Entry}-[func]{} + [class]{Pair}-[func]{} [class]{ArrayHashMap}-[func]{} ``` @@ -514,7 +518,7 @@ $$ === "Swift" ```swift title="array_hash_map.swift" - [class]{Entry}-[func]{} + [class]{Pair}-[func]{} [class]{ArrayHashMap}-[func]{} ``` @@ -522,7 +526,7 @@ $$ === "Zig" ```zig title="array_hash_map.zig" - [class]{Entry}-[func]{} + [class]{Pair}-[func]{} [class]{ArrayHashMap}-[func]{} ``` @@ -530,7 +534,7 @@ $$ === "Dart" ```dart title="array_hash_map.dart" - [class]{Entry}-[func]{} + [class]{Pair}-[func]{} [class]{ArrayHashMap}-[func]{} ``` diff --git a/docs/chapter_hashing/summary.md b/docs/chapter_hashing/summary.md index 7026155c..e26e5f2b 100644 --- a/docs/chapter_hashing/summary.md +++ b/docs/chapter_hashing/summary.md @@ -2,7 +2,7 @@ - 哈希表能够在 $O(1)$ 时间内将键 key 映射到值 value,效率非常高。 - 常见的哈希表操作包括查询、添加与删除键值对、遍历键值对等。 -- 哈希函数将 key 映射为数组索引(桶),以便访问对应的值 value 。 +- 哈希函数将 key 映射为数组索引(桶索引),从而访问对应的值 value 。 - 两个不同的 key 可能在经过哈希函数后得到相同的索引,导致查询结果出错,这种现象被称为哈希冲突。 - 缓解哈希冲突的方法主要有扩容哈希表和优化哈希表的表示方法。 - 负载因子定义为哈希表中元素数量除以桶数量,反映了哈希冲突的严重程度,常用作触发哈希表扩容的条件。与数组扩容类似,哈希表扩容操作也会产生较大的开销。 diff --git a/mkdocs.yml b/mkdocs.yml index b58f21d4..02ef8eb3 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -162,7 +162,7 @@ nav: - 6.     散列表: - chapter_hashing/index.md - 6.1.   哈希表: chapter_hashing/hash_map.md - - 6.2.   哈希冲突处理: chapter_hashing/hash_collision.md + - 6.2.   哈希冲突: chapter_hashing/hash_collision.md - 6.3.   小结: chapter_hashing/summary.md - 7.     树: - chapter_tree/index.md