Skip to content

Commit 7352a6f

Browse files
committed
Added resizable Hash Table
1 parent 496b546 commit 7352a6f

File tree

4 files changed

+210
-0
lines changed

4 files changed

+210
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- [Basic Set](./src/1.BasicSet/Program.cs)
77
- [Hash Table with Open Addressing](./src/2.HashTableWithOpenAddressing/Program.cs)
88
- [Hash Table with Bucket](./src/3.HashTableWithBuckets/Program.cs)
9+
- [Resizing Hash Table (Rehashing)](./src/4.ResizableHashTable/Program.cs)
910

1011

1112
## References

algorithms-data-structures-hashtable.sln

+6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "2.HashTableWithOpenAddressi
99
EndProject
1010
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "3.HashTableWithBuckets", "src\3.HashTableWithBuckets\3.HashTableWithBuckets.csproj", "{A5F2F8DD-B30B-4531-99B1-2C2E4975EC41}"
1111
EndProject
12+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "4.ResizableHashTable", "src\4.ResizableHashTable\4.ResizableHashTable.csproj", "{08CD3BC9-6CCA-46F6-BA70-F0054DD29ECC}"
13+
EndProject
1214
Global
1315
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1416
Debug|Any CPU = Debug|Any CPU
@@ -27,6 +29,10 @@ Global
2729
{A5F2F8DD-B30B-4531-99B1-2C2E4975EC41}.Debug|Any CPU.Build.0 = Debug|Any CPU
2830
{A5F2F8DD-B30B-4531-99B1-2C2E4975EC41}.Release|Any CPU.ActiveCfg = Release|Any CPU
2931
{A5F2F8DD-B30B-4531-99B1-2C2E4975EC41}.Release|Any CPU.Build.0 = Release|Any CPU
32+
{08CD3BC9-6CCA-46F6-BA70-F0054DD29ECC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33+
{08CD3BC9-6CCA-46F6-BA70-F0054DD29ECC}.Debug|Any CPU.Build.0 = Debug|Any CPU
34+
{08CD3BC9-6CCA-46F6-BA70-F0054DD29ECC}.Release|Any CPU.ActiveCfg = Release|Any CPU
35+
{08CD3BC9-6CCA-46F6-BA70-F0054DD29ECC}.Release|Any CPU.Build.0 = Release|Any CPU
3036
EndGlobalSection
3137
GlobalSection(SolutionProperties) = preSolution
3238
HideSolutionNode = FALSE
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
</Project>

src/4.ResizableHashTable/Program.cs

+193
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
// This is a simple implementation of a hash table using open addressing.
2+
// This implementation isn't complete and it's not efficient.
3+
// It's just a simple example to show how a hash table works.
4+
5+
var hashtable = new HashTable<int, Profile>();
6+
7+
hashtable.Add(7, new("Sam", "sam@email.fk"));
8+
hashtable.Add(3, new("Leo", "leo@email.fk"));
9+
hashtable.Add(6, new("Jon", "jon@email.fk"));
10+
//hashtable.Add(3, new("Lia", "lia@email.fk"));
11+
hashtable.Add(4, new("Eva", "eva@email.fk"));
12+
hashtable.Add(0, new("Bob", "bob@email.fk"));
13+
hashtable.Add(1, new("Sue", "sue@email.fk"));
14+
hashtable.Add(2, new("Ana", "ana@email.fk"));
15+
hashtable.Add(5, new("Max", "max@email.fk"));
16+
hashtable.Add(8, new("Kim", "kim@email.fk"));
17+
18+
for (int i = 0; i < 9; i++)
19+
{
20+
Console.WriteLine($"{i} -> {hashtable.Get(i)}");
21+
}
22+
23+
for (int i = 0; i < 9; i++)
24+
{
25+
hashtable.Remove(i);
26+
}
27+
28+
29+
//var hashtable = new HashTable<string, Profile>();
30+
31+
//hashtable.Add("lo", new("Leo", "leo@email.fk"));
32+
//hashtable.Add("la", new("Lia", "lia@email.fk"));
33+
//hashtable.Add("ea", new("Eva", "eva@email.fk"));
34+
//hashtable.Add("bb", new("Bob", "bob@email.fk"));
35+
//hashtable.Add("se", new("Sue", "sue@email.fk"));
36+
//hashtable.Add("aa", new("Ana", "ana@email.fk"));
37+
//hashtable.Add("mx", new("Max", "max@email.fk"));
38+
39+
40+
record Profile(string Name, string Email);
41+
42+
class HashTable<TKey, TValue>
43+
{
44+
record HashNode(TKey Key, TValue Value)
45+
{
46+
public HashNode? Next { get; set; }
47+
}
48+
49+
private HashNode?[] _buckets;
50+
private int _capacity;
51+
private int _count;
52+
53+
54+
public HashTable()
55+
{
56+
_capacity = 4;
57+
_buckets = new HashNode[_capacity];
58+
}
59+
60+
public void Add(TKey key, TValue value)
61+
{
62+
_resizeIfNeeded();
63+
64+
var index = _computeHash(key);
65+
var node = new HashNode(key, value);
66+
67+
if(_buckets[index] is null)
68+
{
69+
_buckets[index] = node;
70+
}
71+
else
72+
{
73+
var current = _buckets[index];
74+
while(current.Next is not null)
75+
{
76+
current = current.Next;
77+
}
78+
79+
if(current.Key.Equals(key))
80+
{
81+
throw new InvalidOperationException("Key already exists");
82+
}
83+
84+
current.Next = node;
85+
}
86+
87+
_count++;
88+
}
89+
90+
public TValue Get(TKey key)
91+
{
92+
var index = _computeHash(key);
93+
var current = _buckets[index];
94+
95+
while(current is not null)
96+
{
97+
if(current.Key.Equals(key))
98+
{
99+
return current.Value;
100+
}
101+
102+
current = current.Next;
103+
}
104+
105+
throw new KeyNotFoundException();
106+
}
107+
108+
public void Remove(TKey key)
109+
{
110+
var index = _computeHash(key);
111+
var current = _buckets[index];
112+
113+
if(current is null)
114+
{
115+
throw new KeyNotFoundException();
116+
}
117+
118+
119+
HashNode? previous = null;
120+
while(current is not null)
121+
{
122+
if(current.Key.Equals(key))
123+
{
124+
if(previous is null)
125+
{
126+
_buckets[index] = current.Next;
127+
}
128+
else
129+
{
130+
previous.Next = current.Next;
131+
}
132+
_count--;
133+
return;
134+
}
135+
136+
previous = current;
137+
current = current.Next;
138+
}
139+
}
140+
141+
private int _computeHash(TKey key)
142+
{
143+
var hash = key.GetHashCode();
144+
hash &= 0x7FFFFFFF; // Remove sinal -> 0x7FFFFFFF = 1111111111111111111111111111111 = int.MaxValue
145+
hash %= _buckets.Length;
146+
return hash;
147+
}
148+
149+
150+
// Also known as rehashing
151+
// Complexity: Time: O(n), Space: O(2n) -> O(n)
152+
private void _resizeIfNeeded()
153+
{
154+
const float LOAD_FACTOR = 0.75f;
155+
156+
if((float)_count / _capacity < LOAD_FACTOR)
157+
{
158+
return;
159+
}
160+
161+
_capacity *= 2; // Double the size
162+
var newBuckets = new HashNode[_capacity];
163+
164+
foreach(var node in _buckets)
165+
{
166+
var current = node;
167+
while(current is not null)
168+
{
169+
var index = _computeHash(current.Key);
170+
var newNode = new HashNode(current.Key, current.Value);
171+
172+
if(newBuckets[index] is null)
173+
{
174+
newBuckets[index] = newNode;
175+
}
176+
else
177+
{
178+
var pivot = newBuckets[index];
179+
while(pivot.Next is not null)
180+
{
181+
pivot = pivot.Next;
182+
}
183+
184+
pivot.Next = newNode;
185+
}
186+
187+
current = current.Next;
188+
}
189+
}
190+
191+
_buckets = newBuckets;
192+
}
193+
}

0 commit comments

Comments
 (0)