diff --git a/codes/ruby/chapter_hashing/array_hash_map.rb b/codes/ruby/chapter_hashing/array_hash_map.rb new file mode 100644 index 000000000..d4dd4cb65 --- /dev/null +++ b/codes/ruby/chapter_hashing/array_hash_map.rb @@ -0,0 +1,121 @@ +=begin +File: array_hash_map.rb +Created Time: 2024-04-13 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### 键值对 ### +class Pair + attr_accessor :key, :val + + def initialize(key, val) + @key = key + @val = val + end +end + +### 基于数组实现的哈希表 ### +class ArrayHashMap + ### 构造方法 ### + def initialize + # 初始化数组,包含 100 个桶 + @buckets = Array.new(100) + end + + ### 哈希函数 ### + def hash_func(key) + index = key % 100 + end + + ### 查询操作 ### + def get(key) + index = hash_func(key) + pair = @buckets[index] + + return if pair.nil? + pair.val + end + + ### 添加操作 ### + def put(key, val) + pair = Pair.new(key, val) + index = hash_func(key) + @buckets[index] = pair + end + + ### 删除操作 ### + def remove(key) + index = hash_func(key) + # 置为 nil ,代表删除 + @buckets[index] = nil + end + + ### 获取所有键值对 ### + def entry_set + result = [] + @buckets.each { |pair| result << pair unless pair.nil? } + result + end + + ### 获取所有键 ### + def key_set + result = [] + @buckets.each { |pair| result << pair.key unless pair.nil? } + result + end + + ### 获取所有值 ### + def value_set + result = [] + @buckets.each { |pair| result << pair.val unless pair.nil? } + result + end + + ### 打印哈希表 ### + def print + @buckets.each { |pair| puts "#{pair.key} -> #{pair.val}" unless pair.nil? } + end +end + +### Driver Code ### +if __FILE__ == $0 + # 初始化哈希表 + hmap = ArrayHashMap.new + + # 添加操作 + # 在哈希表中添加键值对 (key, value) + hmap.put(12836, "小哈") + hmap.put(15937, "小啰") + hmap.put(16750, "小算") + hmap.put(13276, "小法") + hmap.put(10583, "小鸭") + puts "\n添加完成后,哈希表为\nKey -> Value" + hmap.print + + # 查询操作 + # 向哈希表中输入键 key , 得到值 value + name = hmap.get(15937) + puts "\n输入学号 15937 ,查询到姓名 #{name}" + + # 删除操作 + # 在哈希表中删除值对 (key, value) + hmap.remove(10583) + puts "\n删除 10583 后,哈希表为\nKey -> Value" + hmap.print + + # 遍历哈希表 + puts "\n遍历键值对 Key->Value" + for pair in hmap.entry_set + puts "#{pair.key} -> #{pair.val}" + end + + puts "\n单独篇遍历键 Key" + for key in hmap.key_set + puts key + end + + puts "\n单独遍历值 Value" + for val in hmap.value_set + puts val + end +end diff --git a/codes/ruby/chapter_hashing/built_in_hash.rb b/codes/ruby/chapter_hashing/built_in_hash.rb new file mode 100644 index 000000000..6136db50f --- /dev/null +++ b/codes/ruby/chapter_hashing/built_in_hash.rb @@ -0,0 +1,34 @@ +=begin +File: built_in_hash.rb +Created Time: 2024-04-13 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/list_node' + +### Driver Code ### +if __FILE__ == $0 + num = 3 + hash_num = num.hash + puts "整数 #{num} 的哈希值为 #{hash_num}" + + bol = true + hash_bol = bol.hash + puts "布尔量 #{bol} 的哈希值为 #{hash_bol}" + + dec = 3.14159 + hash_dec = dec.hash + puts "小数 #{dec} 的哈希值为 #{hash_dec}" + + str = "Hello 算法" + hash_str = str.hash + puts "字符串 #{str} 的哈希值为 #{hash_str}" + + tup = [12836, '小哈'] + hash_tup = tup.hash + puts "元组 #{tup} 的哈希值为 #{hash_tup}" + + obj = ListNode.new(0) + hash_obj = obj.hash + puts "节点对象 #{obj} 的哈希值为 #{hash_obj}" +end diff --git a/codes/ruby/chapter_hashing/hash_map.rb b/codes/ruby/chapter_hashing/hash_map.rb new file mode 100644 index 000000000..50358af21 --- /dev/null +++ b/codes/ruby/chapter_hashing/hash_map.rb @@ -0,0 +1,44 @@ +=begin +File: hash_map.rb +Created Time: 2024-04-14 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/print_util' + +### Driver Code ### +if __FILE__ == $0 + # 初始化哈希表 + hmap = {} + + # 添加操作 + # 在哈希表中添加键值对 (key, value) + hmap[12836] = "小哈" + hmap[15937] = "小啰" + hmap[16750] = "小算" + hmap[13276] = "小法" + hmap[10583] = "小鸭" + puts "\n添加完成后,哈希表为\nKey -> Value" + print_hash_map(hmap) + + # 查询操作 + # 向哈希表中输入键 key ,得到值 value + name = hmap[15937] + puts "\n输入学号 15937 ,查询到姓名 #{name}" + + # 删除操作 + # 在哈希表中删除键值对 (key, value) + hmap.delete(10583) + puts "\n删除 10583 后,哈希表为\nKey -> Value" + print_hash_map(hmap) + + # 遍历哈希表 + puts "\n遍历键值对 Key->Value" + hmap.entries.each { |key, value| puts "#{key} -> #{value}" } + + puts "\n单独遍历键 Key" + hmap.keys.each { |key| puts key } + + puts "\n单独遍历值 Value" + hmap.values.each { |val| puts val } +end diff --git a/codes/ruby/chapter_hashing/hash_map_chaining.rb b/codes/ruby/chapter_hashing/hash_map_chaining.rb new file mode 100644 index 000000000..bb26c412c --- /dev/null +++ b/codes/ruby/chapter_hashing/hash_map_chaining.rb @@ -0,0 +1,128 @@ +=begin +File: hash_map_chaining.rb +Created Time: 2024-04-13 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative './array_hash_map' + +### 键式地址哈希表 ### +class HashMapChaining + ### 构造方法 ### + def initialize + @size = 0 # 键值对数量 + @capacity = 4 # 哈希表容量 + @load_thres = 2.0 / 3.0 # 触发扩容的负载因子阈值 + @extend_ratio = 2 # 扩容倍数 + @buckets = Array.new(@capacity) { [] } # 桶数组 + end + + ### 哈希函数 ### + def hash_func(key) + key % @capacity + end + + ### 负载因子 ### + def load_factor + @size / @capacity + end + + ### 查询操作 ### + def get(key) + index = hash_func(key) + bucket = @buckets[index] + # 遍历桶,若找到 key ,则返回对应 val + for pair in bucket + return pair.val if pair.key == key + end + # 若未找到 key , 则返回 nil + nil + end + + ### 添加操作 ### + def put(key, val) + # 当负载因子超过阈值时,执行扩容 + extend if load_factor > @load_thres + index = hash_func(key) + bucket = @buckets[index] + # 遍历桶,若遇到指定 key ,则更新对应 val 并返回 + for pair in bucket + if pair.key == key + pair.val = val + return + end + end + # 若无该 key ,则将键值对添加至尾部 + pair = Pair.new(key, val) + bucket << pair + @size += 1 + end + + ### 删除操作 ### + def remove(key) + index = hash_func(key) + bucket = @buckets[index] + # 遍历桶,从中删除键值对 + for pair in bucket + if pair.key == key + bucket.delete(pair) + @size -= 1 + break + end + end + end + + ### 扩容哈希表 ### + def extend + # 暫存原哈希表 + buckets = @buckets + # 初始化扩容后的新哈希表 + @capacity *= @extend_ratio + @buckets = Array.new(@capacity) { [] } + @size = 0 + # 将键值对从原哈希表搬运至新哈希表 + for bucket in buckets + for pair in bucket + put(pair.key, pair.val) + end + end + end + + ### 打印哈希表 ### + def print + for bucket in @buckets + res = [] + for pair in bucket + res << "#{pair.key} -> #{pair.val}" + end + pp res + end + end +end + +### Driver Code ### +if __FILE__ == $0 + ### 初始化哈希表 + hashmap = HashMapChaining.new + + # 添加操作 + # 在哈希表中添加键值对 (key, value) + hashmap.put(12836, "小哈") + hashmap.put(15937, "小啰") + hashmap.put(16750, "小算") + hashmap.put(13276, "小法") + hashmap.put(10583, "小鸭") + puts "\n添加完成后,哈希表为\n[Key1 -> Value1, Key2 -> Value2, ...]" + hashmap.print + + # 查询操作 + # 向哈希表中输入键 key ,得到值 value + name = hashmap.get(13276) + puts "\n输入学号 13276 ,查询到姓名 #{name}" + + # 删除操作 + # 在哈希表中删除键值对 (key, value) + hashmap.remove(12836) + puts "\n删除 12836 后,哈希表为\n[Key1 -> Value1, Key2 -> Value2, ...]" + hashmap.print +end diff --git a/codes/ruby/chapter_hashing/hash_map_open_addressing.rb b/codes/ruby/chapter_hashing/hash_map_open_addressing.rb new file mode 100644 index 000000000..d24d7182f --- /dev/null +++ b/codes/ruby/chapter_hashing/hash_map_open_addressing.rb @@ -0,0 +1,147 @@ +=begin +File: hash_map_open_addressing.rb +Created Time: 2024-04-13 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative './array_hash_map' + +### 开放寻址哈希表 ### +class HashMapOpenAddressing + TOMBSTONE = Pair.new(-1, '-1') # 删除标记 + + ### 构造方法 ### + def initialize + @size = 0 # 键值对数量 + @capacity = 4 # 哈希表容量 + @load_thres = 2.0 / 3.0 # 触发扩容的负载因子阈值 + @extend_ratio = 2 # 扩容倍数 + @buckets = Array.new(@capacity) # 桶数组 + end + + ### 哈希函数 ### + def hash_func(key) + key % @capacity + end + + ### 负载因子 ### + def load_factor + @size / @capacity + end + + ### 搜索 key 对应的桶索引 ### + def find_bucket(key) + index = hash_func(key) + first_tombstone = -1 + # 线性探测,当遇到空桶时跳出 + while !@buckets[index].nil? + # 若遇到 key ,返回对应的桶索引 + if @buckets[index].key == key + # 若之前遇到了删除标记,则将键值对移动至该索引处 + if first_tombstone != -1 + @buckets[first_tombstone] = @buckets[index] + @buckets[index] = TOMBSTONE + return first_tombstone # 返回移动后的桶索引 + end + return index # 返回桶索引 + end + # 记录遇到的首个删除标记 + first_tombstone = index if first_tombstone == -1 && @buckets[index] == TOMBSTONE + # 计算桶索引,越过尾部则返回头部 + index = (index + 1) % @capacity + end + # 若 key 不存在,则返回添加点的索引 + first_tombstone == -1 ? index : first_tombstone + end + + ### 查询操作 ### + def get(key) + # 搜索 key 对应的桶索引 + index = find_bucket(key) + # 若找到键值对,则返回对应 val + return @buckets[index].val unless [nil, TOMBSTONE].include?(@buckets[index]) + # 若键值对不存在,则返回 nil + nil + end + + ### 添加操作 ### + def put(key, val) + # 当负载因子超过阈值时,执行扩容 + extend if load_factor > @load_thres + # 搜索 key 对应的桶索引 + index = find_bucket(key) + # 若找到键值对,则覆盖 val 开返回 + unless [nil, TOMBSTONE].include?(@buckets[index]) + @buckets[index].val = val + return + end + # 若键值对不存在,则添加该键值对 + @buckets[index] = Pair.new(key, val) + @size += 1 + end + + ### 删除操作 ### + def remove(key) + # 搜索 key 对应的桶索引 + index = find_bucket(key) + # 若找到键值对,则用删除标记覆盖它 + unless [nil, TOMBSTONE].include?(@buckets[index]) + @buckets[index] = TOMBSTONE + @size -= 1 + end + end + + ### 扩容哈希表 ### + def extend + # 暂存原哈希表 + buckets_tmp = @buckets + # 初始化扩容后的新哈希表 + @capacity *= @extend_ratio + @buckets = Array.new(@capacity) + @size = 0 + # 将键值对从原哈希表搬运至新哈希表 + for pair in buckets_tmp + put(pair.key, pair.val) unless [nil, TOMBSTONE].include?(pair) + end + end + + ### 打印哈希表 ### + def print + for pair in @buckets + if pair.nil? + puts "Nil" + elsif pair == TOMBSTONE + puts "TOMBSTONE" + else + puts "#{pair.key} -> #{pair.val}" + end + end + end +end + +### Driver Code ### +if __FILE__ == $0 + # 初始化哈希表 + hashmap = HashMapOpenAddressing.new + + # 添加操作 + # 在哈希表中添加键值对 (key, val) + hashmap.put(12836, "小哈") + hashmap.put(15937, "小啰") + hashmap.put(16750, "小算") + hashmap.put(13276, "小法") + hashmap.put(10583, "小鸭") + puts "\n添加完成后,哈希表为\nKey -> Value" + hashmap.print + + # 查询操作 + # 向哈希表中输入键 key ,得到值 val + name = hashmap.get(13276) + puts "\n输入学号 13276 ,查询到姓名 #{name}" + + # 删除操作 + # 在哈希表中删除键值对 (key, val) + hashmap.remove(16750) + puts "\n删除 16750 后,哈希表为\nKey -> Value" + hashmap.print +end diff --git a/codes/ruby/chapter_hashing/simple_hash.rb b/codes/ruby/chapter_hashing/simple_hash.rb new file mode 100644 index 000000000..d24c59e36 --- /dev/null +++ b/codes/ruby/chapter_hashing/simple_hash.rb @@ -0,0 +1,62 @@ +=begin +File: simple_hash.rb +Created Time: 2024-04-14 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### 加法哈希 ### +def add_hash(key) + hash = 0 + modulus = 1_000_000_007 + + key.each_char { |c| hash += c.ord } + + hash % modulus +end + +### 乘法哈希 ### +def mul_hash(key) + hash = 0 + modulus = 1_000_000_007 + + key.each_char { |c| hash = 31 * hash + c.ord } + + hash % modulus +end + +### 异或哈希 ### +def xor_hash(key) + hash = 0 + modulus = 1_000_000_007 + + key.each_char { |c| hash ^= c.ord } + + hash % modulus +end + +### 旋转哈希 ### +def rot_hash(key) + hash = 0 + modulus = 1_000_000_007 + + key.each_char { |c| hash = (hash << 4) ^ (hash >> 28) ^ c.ord } + + hash % modulus +end + +### Driver Code ### +if __FILE__ == $0 + key = "Hello 算法" + + hash = add_hash(key) + puts "加法哈希值为 #{hash}" + + hash = mul_hash(key) + puts "乘法哈希值为 #{hash}" + + hash = xor_hash(key) + puts "异或哈希值为 #{hash}" + + hash = rot_hash(key) + puts "旋转哈希值为 #{hash}" +end diff --git a/codes/ruby/utils/print_util.rb b/codes/ruby/utils/print_util.rb index 59fa42415..52c74a0f8 100644 --- a/codes/ruby/utils/print_util.rb +++ b/codes/ruby/utils/print_util.rb @@ -56,3 +56,8 @@ def print_tree(root, prev=nil, is_right=false) trunk.str = " |" print_tree(root.left, trunk, false) end + +### 打印哈希表 ### +def print_hash_map(hmap) + hmap.entries.each { |key, value| puts "#{key} -> #{value}" } +end diff --git a/docs/chapter_hashing/hash_algorithm.md b/docs/chapter_hashing/hash_algorithm.md index 11d36033e..25fae1539 100644 --- a/docs/chapter_hashing/hash_algorithm.md +++ b/docs/chapter_hashing/hash_algorithm.md @@ -299,7 +299,7 @@ $$ ```rust title="built_in_hash.rs" use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; - + let num = 3; let mut num_hasher = DefaultHasher::new(); num.hash(&mut num_hasher); @@ -374,7 +374,29 @@ $$ === "Ruby" ```ruby title="built_in_hash.rb" + num = 3 + hash_num = num.hash + # 整数 3 的哈希值为 -4385856518450339636 + bol = true + hash_bol = bol.hash + # 布尔量 true 的哈希值为 -1617938112149317027 + + dec = 3.14159 + hash_dec = dec.hash + # 小数 3.14159 的哈希值为 -1479186995943067893 + + str = "Hello 算法" + hash_str = str.hash + # 字符串“Hello 算法”的哈希值为 -4075943250025831763 + + tup = [12836, '小哈'] + hash_tup = tup.hash + # 元组 (12836, '小哈') 的哈希值为 1999544809202288822 + + obj = ListNode.new(0) + hash_obj = obj.hash + # 节点对象 # 的哈希值为 4302940560806366381 ``` === "Zig" diff --git a/docs/chapter_hashing/hash_map.md b/docs/chapter_hashing/hash_map.md index 4766194b0..74d39f1fc 100755 --- a/docs/chapter_hashing/hash_map.md +++ b/docs/chapter_hashing/hash_map.md @@ -31,7 +31,7 @@ ```python title="hash_map.py" # 初始化哈希表 hmap: dict = {} - + # 添加操作 # 在哈希表中添加键值对 (key, value) hmap[12836] = "小哈" @@ -39,11 +39,11 @@ hmap[16750] = "小算" hmap[13276] = "小法" hmap[10583] = "小鸭" - + # 查询操作 # 向哈希表中输入键 key ,得到值 value name: str = hmap[15937] - + # 删除操作 # 在哈希表中删除键值对 (key, value) hmap.pop(10583) @@ -54,7 +54,7 @@ ```cpp title="hash_map.cpp" /* 初始化哈希表 */ unordered_map map; - + /* 添加操作 */ // 在哈希表中添加键值对 (key, value) map[12836] = "小哈"; @@ -62,11 +62,11 @@ map[16750] = "小算"; map[13276] = "小法"; map[10583] = "小鸭"; - + /* 查询操作 */ // 向哈希表中输入键 key ,得到值 value string name = map[15937]; - + /* 删除操作 */ // 在哈希表中删除键值对 (key, value) map.erase(10583); @@ -77,19 +77,19 @@ ```java title="hash_map.java" /* 初始化哈希表 */ Map map = new HashMap<>(); - + /* 添加操作 */ // 在哈希表中添加键值对 (key, value) - map.put(12836, "小哈"); - map.put(15937, "小啰"); - map.put(16750, "小算"); + map.put(12836, "小哈"); + map.put(15937, "小啰"); + map.put(16750, "小算"); map.put(13276, "小法"); map.put(10583, "小鸭"); - + /* 查询操作 */ // 向哈希表中输入键 key ,得到值 value String name = map.get(15937); - + /* 删除操作 */ // 在哈希表中删除键值对 (key, value) map.remove(10583); @@ -108,11 +108,11 @@ { 13276, "小法" }, { 10583, "小鸭" } }; - + /* 查询操作 */ // 向哈希表中输入键 key ,得到值 value string name = map[15937]; - + /* 删除操作 */ // 在哈希表中删除键值对 (key, value) map.Remove(10583); @@ -123,7 +123,7 @@ ```go title="hash_map_test.go" /* 初始化哈希表 */ hmap := make(map[int]string) - + /* 添加操作 */ // 在哈希表中添加键值对 (key, value) hmap[12836] = "小哈" @@ -131,11 +131,11 @@ hmap[16750] = "小算" hmap[13276] = "小法" hmap[10583] = "小鸭" - + /* 查询操作 */ // 向哈希表中输入键 key ,得到值 value name := hmap[15937] - + /* 删除操作 */ // 在哈希表中删除键值对 (key, value) delete(hmap, 10583) @@ -146,7 +146,7 @@ ```swift title="hash_map.swift" /* 初始化哈希表 */ var map: [Int: String] = [:] - + /* 添加操作 */ // 在哈希表中添加键值对 (key, value) map[12836] = "小哈" @@ -154,11 +154,11 @@ map[16750] = "小算" map[13276] = "小法" map[10583] = "小鸭" - + /* 查询操作 */ // 向哈希表中输入键 key ,得到值 value let name = map[15937]! - + /* 删除操作 */ // 在哈希表中删除键值对 (key, value) map.removeValue(forKey: 10583) @@ -176,11 +176,11 @@ map.set(16750, '小算'); map.set(13276, '小法'); map.set(10583, '小鸭'); - + /* 查询操作 */ // 向哈希表中输入键 key ,得到值 value let name = map.get(15937); - + /* 删除操作 */ // 在哈希表中删除键值对 (key, value) map.delete(10583); @@ -200,12 +200,12 @@ map.set(10583, '小鸭'); console.info('\n添加完成后,哈希表为\nKey -> Value'); console.info(map); - + /* 查询操作 */ // 向哈希表中输入键 key ,得到值 value let name = map.get(15937); console.info('\n输入学号 15937 ,查询到姓名 ' + name); - + /* 删除操作 */ // 在哈希表中删除键值对 (key, value) map.delete(10583); @@ -240,7 +240,7 @@ ```rust title="hash_map.rs" use std::collections::HashMap; - + /* 初始化哈希表 */ let mut map: HashMap = HashMap::new(); @@ -272,7 +272,7 @@ ```kotlin title="hash_map.kt" /* 初始化哈希表 */ val map = HashMap() - + /* 添加操作 */ // 在哈希表中添加键值对 (key, value) map[12836] = "小哈" @@ -280,11 +280,11 @@ map[16750] = "小算" map[13276] = "小法" map[10583] = "小鸭" - + /* 查询操作 */ // 向哈希表中输入键 key ,得到值 value val name = map[15937] - + /* 删除操作 */ // 在哈希表中删除键值对 (key, value) map.remove(10583) @@ -293,7 +293,24 @@ === "Ruby" ```ruby title="hash_map.rb" + # 初始化哈希表 + hmap = {} + # 添加操作 + # 在哈希表中添加键值对 (key, value) + hmap[12836] = "小哈" + hmap[15937] = "小啰" + hmap[16750] = "小算" + hmap[13276] = "小法" + hmap[10583] = "小鸭" + + # 查询操作 + # 向哈希表中输入键 key ,得到值 value + name = hmap[15937] + + # 删除操作 + # 在哈希表中删除键值对 (key, value) + hmap.delete(10583) ``` === "Zig" @@ -476,7 +493,7 @@ // 单独遍历键 Key for key in map.keys() { - println!("{key}"); + println!("{key}"); } // 单独遍历值 Value @@ -512,7 +529,15 @@ === "Ruby" ```ruby title="hash_map.rb" + # 遍历哈希表 + # 遍历键值对 key->value + hmap.entries.each { |key, value| puts "#{key} -> #{value}" } + # 单独遍历键 key + hmap.keys.each { |key| puts key } + + # 单独遍历值 value + hmap.values.each { |val| puts val } ``` === "Zig"