CPSC 331: Tutorial 11

Hashing, Collision Resolution and other stuff

PhD Student

Spring 2018

Today

Review, homework help, hot button issues on HW2 (time permitting).

Hash tables

We've seen arrays and lists, these have linear search times, and if you're lucky \(O(log_2(n))\) search time (i.e. a sorted list).

This isn't really that good.

Computers (at least in this decade) have a lot of memory.

DRAM shortages aside, memory is cheap. Can use more memory but get faster data look-ups?

Yes, this is what hash tables do.

Hash tables

null null null null null null null null null null

In practice, hash tables are just arrays.

// In Java, this might look like
Object []hashTable = new Object[hashTableSize];

\(hashTable=\)

We need to make an assumption about objects here, we assume they're hashable.

Objects that are hashable override the hashCode method. If you want to hash an object you defined, you have to implement this method.

Hash tables

null null null null null null null null null
// In Java, this might look like
Object []hashTable = new Object[hashTableSize];

String str1 = "Katherine"; // str1.hashCode() % 10 = 9
String str2 = "Issac";     // str2.hashCode() % 10 = 1
String str3 = "Yohan";

hashTable[str1.hashCode() % 10] = str1;

\(hashTable=\)

"Katherine" (String Object)

Hash tables

null null null null null null null null
// In Java, this might look like
Object []hashTable = new Object[hashTableSize];

String str1 = "Katherine"; // str1.hashCode() % 10 = 9
String str2 = "Isaac";     // str2.hashCode() % 10 = 1
String str3 = "Yohan";

hashTable[str1.hashCode() % 10] = str1;
hashTable[str2.hashCode() % 10] = str2;

\(hashTable=\)

"Katherine"

"Isaac"

Why is this useful?

Hash tables

null null null null null null null null

\(hashTable=\)

"Katherine"

"Isaac"

We use this as a means of creating a mapping between objects. Lookups in this mapping are fast

We mapped a name to itself, we could also map it to data. An example is a cache.

Hash tables

null null null null null null null null

\(hashTable=\)

"Katherine.html" + file data

"Isaac.html" + file data

Think of an HTTP server. If we request "Isaac.html":

  • we the hashCode() of "Isaac.html":
  • check to see if Isaac.html is in the hash table.
  • If it is, we return the data in the table
  • If it isn't we read the file add it to the hash table

This is faster than reading a file from a hard drive

Hash tables

null null null null null null null null
// In Java, this might look like
Object []hashTable = new Object[hashTableSize];

String str1 = "Katherine"; // str1.hashCode() % 10 = 9
String str2 = "Isaac";     // str2.hashCode() % 10 = 1
String str3 = "Yohan";     // str3.hashCode() % 10 = 1

hashTable[str1.hashCode() % 10] = str1;
hashTable[str2.hashCode() % 10] = str2;
hashTable[str3.hashCode() % 10] = str3;

\(hashTable=\)

"Katherine"

"Isaac"

These occupy the same spot in the table? It's a collision.

"Yohan"?

Hash tables

null null null null null null null null

\(hashTable=\)

"Katherine"

"Isaac"

We search for the next open spot in the list and place the object there, this is linear probing

"Yohan"?

null null null null null null null

"Katherine"

"Isaac"

"Yohan"

Hash tables

We search for the next open spot in the list and place the object there, this is linear probing

null null null null null null null

"Katherine"

"Isaac"

"Yohan"

For a given load factor \(\alpha = \frac{\text{number occupied cells}}{\text{table size}}\)

Successful 

Unsuccessful 

\(\frac{1}{2}\left(1 - \frac{1}{1-\alpha} \right) \)

\(\frac{1}{2}\left(1 - \frac{1}{(1-\alpha)^2} \right) \)

Avg # of probes during a search

See lecture slides for details

Hash tables

This tends to cluster entries in the hash table. 

null ? ? null null null null

"Katherine"

"Isaac"

"Yohan"

For a given load factor \(\alpha = \frac{\text{number occupied cells}}{\text{table size}}\)

Successful 

Unsuccessful 

\(\frac{1}{2}\left(1 - \frac{1}{1-\alpha} \right) \)

\(\frac{1}{2}\left(1 - \frac{1}{(1-\alpha)^2} \right) \)

Avg # of probes during a search

Hash tables

 Instead of trying to insert into the H(str) + 1, H(str)+2, H(str)+3... We insert quadratically, i.e. H(str) + \(1^2\), H(str)-\(2^2\), H(str)+\(3^2\)...

For a given load factor \(\alpha = \frac{\text{number occupied cells}}{\text{table size}}\)

Successful 

Unsuccessful 

\(1 - ln(1-\alpha) - \frac{\alpha}{2}\)

\(\frac{1}{1-\alpha} - \alpha - ln(1-\alpha)\)

Avg # of probes during a search

This is quadratic probing

Notice the - sign, we alternate between +/-

Hash tables

 Instead of trying to insert into the \(H\)(str) + 1, \(H\)(str)+2, \(H\)(str)+3... We use a second hash function \(H_2\) and try to insert at \(H\)(str) + \(H_2\)(str), \(H\)(str) + 2\(H_2\)(str), \(H\)(str) + 3\(H_2\)(str)...

For a given load factor \(\alpha = \frac{\text{number occupied cells}}{\text{table size}}\)

Successful 

Unsuccessful 

\(\frac{1}{\alpha}ln\left(\frac{1}{1-\alpha}\right)\)

\(\frac{1}{1-\alpha} \)

Avg # of probes during a search

This is double hashing

Hash tables

null null null null null null null null

\(hashTable=\)

"Katherine"

"Isaac"

Don't worry about probing, so keep entries in a linked list

"Yohan"

Hash tables

// In Java, this might look like
ArrayList<LinkedList<Object>> hashTable = 
      new  ArrayList<LinkedList<Object>>(10);

for (int i = 0; i < 10; i++) {
    hashTable.add(new LinkedList<Object>());
}
        
String str1 = "Katherine"; // str1.hashCode() % 10 = 9
String str2 = "Isaac";     // str2.hashCode() % 10 = 1
String str3 = "Yohan";     // str3.hashCode() % 10 = 1

hashTable.get(str1.hashCode() % 10).add(str1);
hashTable.get(str2.hashCode() % 10).add(str2);
hashTable.get(str3.hashCode() % 10).add(str3);

This is with a linked list

Hash tables

// In Java, this might look like
ArrayList<ArrayList<Object>> hashTable = 
      new  ArrayList<ArrayList<Object>>(10);

for (int i = 0; i < 10; i++) {
    hashTable.add(new ArrayList<Object>());
}
        
String str1 = "Katherine"; // str1.hashCode() % 10 = 9
String str2 = "Isaac";     // str2.hashCode() % 10 = 1
String str3 = "Yohan";     // str3.hashCode() % 10 = 1

hashTable.get(str1.hashCode() % 10).add(str1);
hashTable.get(str2.hashCode() % 10).add(str2);
hashTable.get(str3.hashCode() % 10).add(str3);

This is with a resizable list

Hash tables

null null null null null null null null

\(hashTable=\)

"Katherine"

"Isaac"

"Yohan"

If we're searching for something in the hash table, we hash the object, then search the list. This is done in \(\Theta(1+\alpha)\) time. What if this list was sorted? What about deletion?

Heaps

A heap is a binary tree that is perfectly balanced, with the property that a node is greater than all of its children (max heap)

10

3

5

1

2

As an array this looks like A=[10,3,5,1,2]

Heapsort

Take the top of the tree off the heap, swap it with the last element, and reheap-ify

10

3

5

1

2

As an array we swap with the last element, and reheap-ify on a smaller list.

Heapsort

Take the top of the tree off the heap, swap it with the last element, and reheap-ify

10

3

5

1

2

As an array this looks like A=[2,3,5,1,10]

Heapsort

Take the top of the tree off the heap, swap it with the last element, and reheap-ify

10

3

5

1

2

As an array this looks like A=[5,2,3,1,10]

Heapsort

Take the top of the tree off the heap, swap it with the last element, and reheap-ify

10

3

5

1

2

As an array this looks like A=[1,2,3,5,10]

Heapsort

Keep doing this... (our sorted array grows from n-1 down to 0)

10

3

5

1

2

As an array this looks like A=[1,2,3,5,10]

CPSC 331: Tutorial 11

By Joshua Horacsek

CPSC 331: Tutorial 11

  • 1,104