Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
366 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# Hash table | ||
- key value pair implementation of dictionary ADT | ||
|
||
## operations | ||
- insert | ||
- remove | ||
- find | ||
|
||
## Hash table techniques | ||
- Direct addressing | ||
- Memory wastage | ||
- only support integer keys | ||
- Open addressing | ||
|
||
## Hash function | ||
- Maps keys to indices of array | ||
- Comprise of | ||
- Hash code map(use to convert key to integer value) | ||
- Compression map(use to compress the key value so that it lies in the hash table boundaries) | ||
|
||
## A good hash function | ||
- Quick to compute | ||
- A good hash function distributes keys uniformly across the hash table | ||
- Minimizes the probability of collision | ||
- It should map same key to same index | ||
|
||
## Hash-Code Maps (hashing techniques) | ||
- Integer cast | ||
- For numeric types less than 32bits | ||
- If key is less than 32bits | ||
- Component sum | ||
- For numeric types with more than 32 bits(eg long, double) | ||
- Bad for strings | ||
- As two strings can have the same characters | ||
- Polynomial accumulation | ||
- For strings of a natural language, combine the character values(ASCII) a0,a1,a2...a(n-1) by viewing them as coefficient of a polynomial: a0 + a1*x + ... + x^n-1 * a(n-1) | ||
- Choices of x = 33, 37, 39 or 41 gives at most 6 collisions on a vocabulary of 50,000 english words | ||
|
||
## Compression Maps | ||
- Use the remainder | ||
- h(k) = k mod m, k is the key and m is the size of table | ||
- h(k) lies in between 0 and m - 1 | ||
- Bad | ||
- if m is power of 2 let say e, then h(k) gives the e least significant bits of k | ||
- all keys with the same ending goes to the same place | ||
- Good | ||
- if m is prime and not too close to exact power of 2 | ||
- helps ensure uniform distribution | ||
- Use | ||
- h(k) = Floor(m(kA mod 1)) | ||
- k is the key, m is the table size | ||
- 0 < A < 1 | ||
- m can be size of 2^p | ||
- Optimal choice of A depends on the characteristics of the data | ||
- knuth says use | ||
- Fibonacci hashing | ||
- Conjugate of the golder ratio | ||
- (under-root(5) - 1) / 2 | ||
- Multiply, Add and Divide (MAD) | ||
- h(k) = | ak + b | mod N | ||
- k is the key, | ||
- a should not be the multiple of N | ||
- Same formula use in (pseudo) random number generators | ||
|
||
## Universal hashing | ||
- Choose random hash function from the set of multiple hash function at the init of the hash table | ||
|
||
## Collision resolution techniques | ||
- Chaining (using linked list) | ||
- Open Addressing | ||
- All elements are stored in the table i.e n <= m | ||
- Each table entry can contains either an element or null | ||
- Linear probing | ||
- Double hashing | ||
|
||
## Load factor | ||
- Given hash table T with m slots holding n elements, the load factor is defined as a = n/m |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
const { hashCodePoly } = require('../utils/hash') | ||
|
||
class HashMap { | ||
constructor (hashFn = hashCodePoly) { | ||
this.array = new Array(1) | ||
this.length = 0 | ||
this.hash = hashFn | ||
} | ||
|
||
_resize () { | ||
const newArray = new Array(this.array.length * 2) | ||
|
||
this.array.forEach(item => { | ||
if (item === undefined) { | ||
return | ||
} | ||
|
||
item.forEach(([key, value]) => { | ||
const idx = this.hash(key) % newArray.length | ||
|
||
if (newArray[idx] === undefined) { | ||
newArray[idx] = [] | ||
} | ||
|
||
newArray[idx].push([key, value]) | ||
}) | ||
}) | ||
|
||
this.array = newArray | ||
} | ||
|
||
set (key, value) { | ||
const loadFactor = this.length / this.array.length | ||
|
||
if (loadFactor > 0.8) { | ||
this._resize() | ||
} | ||
|
||
const idx = this.hash(key) % this.array.length | ||
|
||
if (this.has(key)) { | ||
this.array[idx] = this.array[idx].map((i) => { | ||
if (i[0] === key) { | ||
return [i[0], value] | ||
} | ||
|
||
return i | ||
}) | ||
} else { | ||
if (this.array[idx] === undefined) { | ||
this.array[idx] = [] | ||
} | ||
|
||
this.array[idx].push([key, value]) | ||
this.length++ | ||
} | ||
} | ||
|
||
get (key) { | ||
if (!this.has(key)) { | ||
return -1 | ||
} | ||
|
||
const idx = this.hash(key) % this.array.length | ||
for (let i = 0; i < this.array[idx].length; i++) { | ||
const [k, value] = this.array[idx][i] | ||
|
||
if (k === key) { | ||
return value | ||
} | ||
} | ||
} | ||
|
||
delete (key) { | ||
if (!this.has(key)) { | ||
return | ||
} | ||
|
||
const idx = this.hash(key) % this.array.length | ||
this.array[idx] = this.array[idx].filter(([k]) => k !== key) | ||
this.length-- | ||
} | ||
|
||
has (key) { | ||
const idx = this.hash(key) % this.array.length | ||
|
||
if (this.array[idx] === undefined) { | ||
return false | ||
} | ||
|
||
if (this.array[idx].find(([k]) => k === key) === undefined) { | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
size () { | ||
return this.length | ||
} | ||
} | ||
|
||
module.exports = HashMap |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
const HashMap = require('./chaining') | ||
|
||
test('set and get', () => { | ||
const map = new HashMap() | ||
map.set('name', 'red') | ||
expect(map.get('name')).toBe('red') | ||
}) | ||
|
||
test('has', () => { | ||
const map = new HashMap() | ||
map.set('hi', 'john') | ||
expect(map.has('hi')).toBe(true) | ||
expect(map.has('red')).toBe(false) | ||
}) | ||
|
||
test('delete', () => { | ||
const map = new HashMap() | ||
map.set('hi', 'john') | ||
map.delete('hi') | ||
expect(map.has('hi')).toBe(false) | ||
}) | ||
|
||
test('size', () => { | ||
const map = new HashMap() | ||
map.set('hi', 'john') | ||
expect(map.size()).toBe(1) | ||
}) | ||
|
||
test('resize', () => { | ||
const map = new HashMap() | ||
map.set('name', 'john') | ||
map.set('age', 5) | ||
map.set('dob', '1/2/3') | ||
expect(map.array.length).toBe(4) | ||
}) | ||
|
||
test('case 1', () => { | ||
const map = new HashMap() | ||
map.set(1, 1) | ||
map.set(2, 2) | ||
expect(map.get(1)).toBe(1) | ||
expect(map.get(3)).toBe(-1) | ||
map.set(2, 1) | ||
expect(map.get(2)).toBe(1) | ||
map.delete(2) | ||
expect(map.get(2)).toBe(-1) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
const HashMapChaining = require('./chaining') | ||
|
||
module.exports = HashMapChaining |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
const { hashCodePoly } = require('../utils/hash') | ||
|
||
class HashMap { | ||
constructor (size, hashFn = hashCodePoly) { | ||
this.array = new Array(size).fill(null) | ||
this.size = size | ||
this.total = 0 | ||
this.X = [-Infinity, -Infinity] | ||
this.hashCode = hashFn | ||
} | ||
|
||
searchEmptyIndex (key) { | ||
const intKey = this.hashCode(key) | ||
let index = (intKey % this.size) | ||
|
||
while (this.array[index] !== null && this.array[index] !== this.X) { | ||
index = (index + 1) % this.size | ||
} | ||
|
||
return index | ||
} | ||
|
||
get (key) { | ||
const intKey = this.hashCode(key) | ||
let index = (intKey % this.size) | ||
const init = index | ||
|
||
while (this.array[index] !== null) { | ||
if (this.array[index][0] === intKey) { | ||
return this.array[index][1] | ||
} | ||
|
||
index = (index + 1) % this.size | ||
|
||
if (init === index) { | ||
return | ||
} | ||
} | ||
} | ||
|
||
set (key, value) { | ||
if (this.total === this.size) { | ||
throw new Error('Hash table is full') | ||
} | ||
|
||
const intKey = this.hashCode(key) | ||
let index = this.searchEmptyIndex(key) | ||
|
||
this.array[index] = [intKey, value] | ||
this.total += 1 | ||
} | ||
|
||
remove (key) { | ||
const intKey = this.hashCode(key) | ||
let index = (intKey % this.size) | ||
const init = index | ||
|
||
while (this.array[index]) { | ||
if (this.array[index][0] === intKey) { | ||
this.array[index] = this.X | ||
this.total -= 1 | ||
return | ||
} | ||
|
||
index = (index + 1) % this.size | ||
|
||
if (index === init) { | ||
return | ||
} | ||
} | ||
} | ||
} | ||
|
||
module.exports = HashMap |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
const HashTable = require('./linear-probing') | ||
|
||
test('set and get', () => { | ||
const map = new HashTable(13) | ||
map.set('john', 43) | ||
|
||
expect(map.get('john')).toBe(43) | ||
}) | ||
|
||
test('remove', () => { | ||
const map = new HashTable(13) | ||
map.set('john', 43) | ||
map.remove('john') | ||
|
||
expect(map.get('john')).toBe(undefined) | ||
}) | ||
|
||
test('table full check', () => { | ||
const map = new HashTable(1) | ||
map.set('john', 43) | ||
|
||
expect(() => map.set('red john', 43)).toThrowError('full') | ||
}) |
Oops, something went wrong.