From 29c5ff4a8f271c32fcce539e22d9b8604e26f34c Mon Sep 17 00:00:00 2001 From: hpstory <33348162+hpstory@users.noreply.github.com> Date: Sun, 24 Sep 2023 03:15:23 +0800 Subject: [PATCH] feat(csharp): add forLoopRecur and new HashMapOpenAddressing (#783) --- .../recursion.cs | 22 ++++ .../hash_map_open_addressing.cs | 116 +++++++++--------- 2 files changed, 82 insertions(+), 56 deletions(-) diff --git a/codes/csharp/chapter_computational_complexity/recursion.cs b/codes/csharp/chapter_computational_complexity/recursion.cs index 932624ee..1e733753 100644 --- a/codes/csharp/chapter_computational_complexity/recursion.cs +++ b/codes/csharp/chapter_computational_complexity/recursion.cs @@ -18,6 +18,25 @@ public class recursion { return n + res; } + /* 使用迭代模拟递归 */ + public int forLoopRecur(int n) { + // 使用一个显式的栈来模拟系统调用栈 + Stack stack = new Stack(); + int res = 0; + // 递:递归调用 + for (int i = n; i > 0; i--) { + // 通过“入栈操作”模拟“递” + stack.Push(i); + } + // 归:返回结果 + while (stack.Count > 0) { + // 通过“出栈操作”模拟“归” + res += stack.Pop(); + } + // res = 1+2+3+...+n + return res; + } + /* 尾递归 */ public int tailRecur(int n, int res) { // 终止条件 @@ -47,6 +66,9 @@ public class recursion { res = recur(n); Console.WriteLine("\n递归函数的求和结果 res = " + res); + res = forLoopRecur(n); + Console.WriteLine("\n使用迭代模拟递归求和结果 res = " + res); + res = tailRecur(n, 0); Console.WriteLine("\n尾递归函数的求和结果 res = " + res); diff --git a/codes/csharp/chapter_hashing/hash_map_open_addressing.cs b/codes/csharp/chapter_hashing/hash_map_open_addressing.cs index 7fb6b615..e9f062d7 100644 --- a/codes/csharp/chapter_hashing/hash_map_open_addressing.cs +++ b/codes/csharp/chapter_hashing/hash_map_open_addressing.cs @@ -8,21 +8,17 @@ namespace hello_algo.chapter_hashing; /* 开放寻址哈希表 */ class HashMapOpenAddressing { - int size; // 键值对数量 - int capacity; // 哈希表容量 - double loadThres; // 触发扩容的负载因子阈值 - int extendRatio; // 扩容倍数 - Pair[] buckets; // 桶数组 - Pair removed; // 删除标记 + private int size; // 键值对数量 + private int capacity = 4; // 哈希表容量 + private double loadThres = 2.0 / 3; // 触发扩容的负载因子阈值 + private int extendRatio = 2; // 扩容倍数 + private Pair[] buckets; // 桶数组 + private Pair TOMBSTONE = new Pair(-1, "-1"); // 删除标记 /* 构造方法 */ public HashMapOpenAddressing() { size = 0; - capacity = 4; - loadThres = 2.0 / 3.0; - extendRatio = 2; buckets = new Pair[capacity]; - removed = new Pair(-1, "-1"); } /* 哈希函数 */ @@ -35,20 +31,42 @@ class HashMapOpenAddressing { return (double)size / capacity; } + /* 搜索 key 对应的桶索引 */ + private int findBucket(int key) { + int index = hashFunc(key); + int firstTombstone = -1; + // 线性探测,当遇到空桶时跳出 + while (buckets[index] != null) { + // 若遇到 key ,返回对应桶索引 + if (buckets[index].key == key) { + // 若之前遇到了删除标记,则将键值对移动至该索引 + if (firstTombstone != -1) { + buckets[firstTombstone] = buckets[index]; + buckets[index] = TOMBSTONE; + return firstTombstone; // 返回移动后的桶索引 + } + return index; // 返回桶索引 + } + // 记录遇到的首个删除标记 + if (firstTombstone == -1 && buckets[index] == TOMBSTONE) { + firstTombstone = index; + } + // 计算桶索引,越过尾部返回头部 + index = (index + 1) % capacity; + } + // 若 key 不存在,则返回添加点的索引 + return firstTombstone == -1 ? index : firstTombstone; + } + /* 查询操作 */ 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; + // 搜索 key 对应的桶索引 + int index = findBucket(key); + // 若找到键值对,则返回对应 val + if (buckets[index] != null && buckets[index] != TOMBSTONE) { + return buckets[index].val; } + // 若键值对不存在,则返回 null return null; } @@ -58,42 +76,26 @@ class HashMapOpenAddressing { 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; - } + // 搜索 key 对应的桶索引 + int index = findBucket(key); + // 若找到键值对,则覆盖 val 并返回 + if (buckets[index] != null && buckets[index] != TOMBSTONE) { + buckets[index].val = val; + return; } + // 若键值对不存在,则添加该键值对 + buckets[index] = new Pair(key, val); + size++; } /* 删除操作 */ 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; - } + // 搜索 key 对应的桶索引 + int index = findBucket(key); + // 若找到键值对,则用删除标记覆盖它 + if (buckets[index] != null && buckets[index] != TOMBSTONE) { + buckets[index] = TOMBSTONE; + size--; } } @@ -107,7 +109,7 @@ class HashMapOpenAddressing { size = 0; // 将键值对从原哈希表搬运至新哈希表 foreach (Pair pair in bucketsTmp) { - if (pair != null && pair != removed) { + if (pair != null && pair != TOMBSTONE) { put(pair.key, pair.val); } } @@ -116,10 +118,12 @@ class HashMapOpenAddressing { /* 打印哈希表 */ public void print() { foreach (Pair pair in buckets) { - if (pair != null) { - Console.WriteLine(pair.key + " -> " + pair.val); - } else { + if (pair == null) { Console.WriteLine("null"); + } else if (pair == TOMBSTONE) { + Console.WriteLine("TOMBSTONE"); + } else { + Console.WriteLine(pair.key + " -> " + pair.val); } } }