diff --git a/codes/csharp/chapter_hashing/built_in_hash.cs b/codes/csharp/chapter_hashing/built_in_hash.cs new file mode 100644 index 000000000..062a5e8d5 --- /dev/null +++ b/codes/csharp/chapter_hashing/built_in_hash.cs @@ -0,0 +1,36 @@ +/** +* File: built_in_hash.cs +* Created Time: 2023-06-26 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_hashing; + +public class built_in_hash { + [Test] + public void Test() { + int num = 3; + int hashNum = num.GetHashCode(); + Console.WriteLine("整数 " + num + " 的哈希值为 " + hashNum); + + bool bol = true; + int hashBol = bol.GetHashCode(); + Console.WriteLine("布尔量 " + bol + " 的哈希值为 " + hashBol); + + double dec = 3.14159; + int hashDec = dec.GetHashCode(); + Console.WriteLine("小数 " + dec + " 的哈希值为 " + hashDec); + + string str = "Hello 算法"; + int hashStr = str.GetHashCode(); + Console.WriteLine("字符串 " + str + " 的哈希值为 " + hashStr); + + object[] arr = { 12836, "小哈" }; + int hashTup = arr.GetHashCode(); + Console.WriteLine("数组 [" + string.Join(", ", arr) + "] 的哈希值为 " + hashTup); + + ListNode obj = new ListNode(0); + int hashObj = obj.GetHashCode(); + Console.WriteLine("节点对象 " + obj + " 的哈希值为 " + hashObj); + } +} diff --git a/codes/csharp/chapter_hashing/hash_map_chaining.cs b/codes/csharp/chapter_hashing/hash_map_chaining.cs new file mode 100644 index 000000000..d728aea42 --- /dev/null +++ b/codes/csharp/chapter_hashing/hash_map_chaining.cs @@ -0,0 +1,143 @@ +/** +* File: hash_map_chaining.cs +* Created Time: 2023-06-26 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_hashing; + +/* 链式地址哈希表 */ +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 List>(capacity); + for (int i = 0; i < capacity; i++) { + buckets.Add(new List()); + } + } + + /* 哈希函数 */ + private int hashFunc(int key) { + return key % capacity; + } + + /* 负载因子 */ + private double loadFactor() { + return (double)size / capacity; + } + + /* 查询操作 */ + public string get(int key) { + int index = hashFunc(key); + // 遍历桶,若找到 key 则返回对应 val + foreach (Pair pair in buckets[index]) { + if (pair.key == key) { + return pair.val; + } + } + // 若未找到 key 则返回 null + return null; + } + + /* 添加操作 */ + public void put(int key, string val) { + // 当负载因子超过阈值时,执行扩容 + if (loadFactor() > loadThres) { + extend(); + } + int index = hashFunc(key); + // 遍历桶,若遇到指定 key ,则更新对应 val 并返回 + foreach (Pair pair in buckets[index]) { + if (pair.key == key) { + pair.val = val; + return; + } + } + // 若无该 key ,则将键值对添加至尾部 + buckets[index].Add(new Pair(key, val)); + size++; + } + + /* 删除操作 */ + public void remove(int key) { + int index = hashFunc(key); + // 遍历桶,从中删除键值对 + foreach (Pair pair in buckets[index].ToList()) { + if (pair.key == key) { + buckets[index].Remove(pair); + } + } + size--; + } + + /* 扩容哈希表 */ + private void extend() { + // 暂存原哈希表 + List> bucketsTmp = buckets; + // 初始化扩容后的新哈希表 + capacity *= extendRatio; + buckets = new List>(capacity); + for (int i = 0; i < capacity; i++) { + buckets.Add(new List()); + } + size = 0; + // 将键值对从原哈希表搬运至新哈希表 + foreach (List bucket in bucketsTmp) { + foreach (Pair pair in bucket) { + put(pair.key, pair.val); + } + } + } + + /* 打印哈希表 */ + public void print() { + foreach (List bucket in buckets) { + List res = new List(); + foreach (Pair pair in bucket) { + res.Add(pair.key + " -> " + pair.val); + } + foreach (string kv in res) { + Console.WriteLine(kv); + } + } + } +} + +public class hash_map_chaining { + [Test] + public void Test() { + /* 初始化哈希表 */ + HashMapChaining map = new HashMapChaining(); + + /* 添加操作 */ + // 在哈希表中添加键值对 (key, value) + map.put(12836, "小哈"); + map.put(15937, "小啰"); + map.put(16750, "小算"); + map.put(13276, "小法"); + map.put(10583, "小鸭"); + Console.WriteLine("\n添加完成后,哈希表为\nKey -> Value"); + map.print(); + + /* 查询操作 */ + // 向哈希表输入键 key ,得到值 value + string name = map.get(13276); + Console.WriteLine("\n输入学号 13276 ,查询到姓名 " + name); + + /* 删除操作 */ + // 在哈希表中删除键值对 (key, value) + map.remove(12836); + Console.WriteLine("\n删除 12836 后,哈希表为\nKey -> Value"); + map.print(); + } +} \ No newline at end of file diff --git a/codes/csharp/chapter_hashing/hash_map_open_addressing.cs b/codes/csharp/chapter_hashing/hash_map_open_addressing.cs new file mode 100644 index 000000000..7fb6b615d --- /dev/null +++ b/codes/csharp/chapter_hashing/hash_map_open_addressing.cs @@ -0,0 +1,155 @@ +/** +* File: hash_map_open_addressing.cs +* Created Time: 2023-06-26 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_hashing; + +/* 开放寻址哈希表 */ +class HashMapOpenAddressing { + int size; // 键值对数量 + int capacity; // 哈希表容量 + double loadThres; // 触发扩容的负载因子阈值 + int extendRatio; // 扩容倍数 + Pair[] buckets; // 桶数组 + Pair removed; // 删除标记 + + /* 构造方法 */ + public HashMapOpenAddressing() { + size = 0; + capacity = 4; + loadThres = 2.0 / 3.0; + extendRatio = 2; + buckets = new Pair[capacity]; + removed = new Pair(-1, "-1"); + } + + /* 哈希函数 */ + private int hashFunc(int key) { + return key % capacity; + } + + /* 负载因子 */ + private 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; + } + } + } + + /* 扩容哈希表 */ + private void extend() { + // 暂存原哈希表 + Pair[] bucketsTmp = buckets; + // 初始化扩容后的新哈希表 + capacity *= extendRatio; + buckets = new Pair[capacity]; + size = 0; + // 将键值对从原哈希表搬运至新哈希表 + foreach (Pair pair in bucketsTmp) { + if (pair != null && pair != removed) { + put(pair.key, pair.val); + } + } + } + + /* 打印哈希表 */ + public void print() { + foreach (Pair pair in buckets) { + if (pair != null) { + Console.WriteLine(pair.key + " -> " + pair.val); + } else { + Console.WriteLine("null"); + } + } + } +} + +public class hash_map_open_addressing { + [Test] + public void Test() { + /* 初始化哈希表 */ + HashMapOpenAddressing map = new HashMapOpenAddressing(); + + /* 添加操作 */ + // 在哈希表中添加键值对 (key, value) + map.put(12836, "小哈"); + map.put(15937, "小啰"); + map.put(16750, "小算"); + map.put(13276, "小法"); + map.put(10583, "小鸭"); + Console.WriteLine("\n添加完成后,哈希表为\nKey -> Value"); + map.print(); + + /* 查询操作 */ + // 向哈希表输入键 key ,得到值 value + string name = map.get(13276); + Console.WriteLine("\n输入学号 13276 ,查询到姓名 " + name); + + /* 删除操作 */ + // 在哈希表中删除键值对 (key, value) + map.remove(16750); + Console.WriteLine("\n删除 16750 后,哈希表为\nKey -> Value"); + map.print(); + } +} diff --git a/codes/csharp/chapter_hashing/simple_hash.cs b/codes/csharp/chapter_hashing/simple_hash.cs new file mode 100644 index 000000000..84542c885 --- /dev/null +++ b/codes/csharp/chapter_hashing/simple_hash.cs @@ -0,0 +1,66 @@ +/** +* File: simple_hash.cs +* Created Time: 2023-06-26 +* Author: hpstory (hpstory1024@163.com) +*/ + +namespace hello_algo.chapter_hashing; + +public class simple_hash { + /* 加法哈希 */ + public static int addHash(string key) { + long hash = 0; + const int MODULUS = 1000000007; + foreach (char c in key) { + hash = (hash + c) % MODULUS; + } + return (int)hash; + } + + /* 乘法哈希 */ + public static int mulHash(string key) { + long hash = 0; + const int MODULUS = 1000000007; + foreach (char c in key) { + hash = (31 * hash + c) % MODULUS; + } + return (int)hash; + } + + /* 异或哈希 */ + public static int xorHash(string key) { + int hash = 0; + const int MODULUS = 1000000007; + foreach (char c in key) { + hash ^= c; + } + return hash & MODULUS; + } + + /* 旋转哈希 */ + public static int rotHash(string key) { + long hash = 0; + const int MODULUS = 1000000007; + foreach (char c in key) { + hash = ((hash << 4) ^ (hash >> 28) ^ c) % MODULUS; + } + return (int)hash; + } + + [Test] + public void Test() { + string key = "Hello 算法"; + + int hash = addHash(key); + Console.WriteLine("加法哈希值为 " + hash); + + hash = mulHash(key); + Console.WriteLine("乘法哈希值为 " + hash); + + hash = xorHash(key); + Console.WriteLine("异或哈希值为 " + hash); + + hash = rotHash(key); + Console.WriteLine("旋转哈希值为 " + hash); + } +} diff --git a/docs/chapter_hashing/hash_algorithm.md b/docs/chapter_hashing/hash_algorithm.md index 074023eac..f48513f40 100644 --- a/docs/chapter_hashing/hash_algorithm.md +++ b/docs/chapter_hashing/hash_algorithm.md @@ -338,7 +338,29 @@ $$ === "C#" ```csharp title="built_in_hash.cs" + int num = 3; + int hashNum = num.GetHashCode(); + // 整数 3 的哈希值为 3; + + bool bol = true; + int hashBol = bol.GetHashCode(); + // 布尔量 true 的哈希值为 1; + + double dec = 3.14159; + int hashDec = dec.GetHashCode(); + // 小数 3.14159 的哈希值为 -1340954729; + string str = "Hello 算法"; + int hashStr = str.GetHashCode(); + // 字符串 Hello 算法 的哈希值为 -586107568; + + object[] arr = { 12836, "小哈" }; + int hashTup = arr.GetHashCode(); + // 数组 [12836, 小哈] 的哈希值为 42931033; + + ListNode obj = new ListNode(0); + int hashObj = obj.GetHashCode(); + // 节点对象 0 的哈希值为 39053774; ``` === "Swift"