{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "fdcf04c6",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Hash Table Data Structure, Implementing Set and Map ADTs\n",
    "\n",
    "#### CS 66: Introduction to Computer Science II"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fa72fac7",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## References for this lecture\n",
    "\n",
    "Problem Solving with Algorithms and Data Structures using Python\n",
    "\n",
    "Section 6.5 [https://runestone.academy/ns/books/published/pythonds/SortSearch/Hashing.html](https://runestone.academy/ns/books/published/pythonds/SortSearch/Hashing.html)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9215744a",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Review: Hash Tables\n",
    "\n",
    "Hash tables work like the above approach, except it allows for values outside of indices of the table using a __hash function__.\n",
    "\n",
    "Hash functions can be any function that transforms a value into a suitable index for the list. \n",
    "\n",
    "For example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "7c6d220d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Initialized table; [None, None, None, None, None, None, None, None, None, None]\n",
      "The hash table with some data in it\n",
      "[None, None, None, None, 1004, None, None, 87, None, None]\n"
     ]
    }
   ],
   "source": [
    "hash_table_size = 10\n",
    "hash_table = [None] * 10 #initialize all 10 spots to None\n",
    "\n",
    "def simple_hash(value,num_items):\n",
    "    return value % num_items\n",
    "\n",
    "print(\"Initialized table;\",hash_table)\n",
    "\n",
    "#putting the value 87 into the hash table\n",
    "hash_table[ simple_hash(87,hash_table_size) ] = 87\n",
    "#putting the value 1004 into the hash table\n",
    "hash_table[ simple_hash(1004,hash_table_size) ] = 1004\n",
    "\n",
    "print(\"The hash table with some data in it\")\n",
    "print(hash_table)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "451d5451",
   "metadata": {},
   "source": [
    "## Group Activity Problem 1 (Review) \n",
    "\n",
    "What line of code would I use to check if a number like 87 or 63 or 37 is in the hash table? What is the Big O of this?"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7dbdd095",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Review: Collisions\n",
    "\n",
    "When two different items end up with the same hash value, it is called a __collision__.\n",
    "\n",
    "This is a problem since both items can't occupy the same memory location.\n",
    "\n",
    "__Collision Resolution__ is the process of finding a new location for an item when there is a collision.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "21f838ed",
   "metadata": {},
   "source": [
    "## Linear Probing\n",
    "\n",
    "__Linear probing:__ collision resolution strategy - put the item in the next available spot.\n",
    "\n",
    "Let's see what would happen if we put the following items into our hash table of size 10:\n",
    "\n",
    "13, 56, 193, 84, 73, 264"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "deda1b33",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "## Group Activity Problem 2 -  a bigger example\n",
    "\n",
    "Make a copy of this spreadsheet and put the following numbers into the hash table:\n",
    "\n",
    "[https://docs.google.com/spreadsheets/d/11hRzwujbkeBGmGGDXgaZrExLDNBN7_dM5Y1X25nmJcU/edit?usp=sharing](https://docs.google.com/spreadsheets/d/11hRzwujbkeBGmGGDXgaZrExLDNBN7_dM5Y1X25nmJcU/edit?usp=sharing)\n",
    "\n",
    "157, 345, 206, 721, 300, 111, 897, 66, 578, 416, 126\n",
    "\n",
    "Answer the following question:\n",
    "* What are some ways we make collisions less likely?\n",
    "* What problem do you run into if you try to remove items from the table?\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "764d02d4",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Rehashing\n",
    "\n",
    "__Rehashing:__ use another function to find a new spot when a collision happens\n",
    "\n",
    "* linear probing is a case of rehashinig\n",
    "\n",
    "* _skip n_ - instead of jumping to next slot, skip down $n$ slots and try it there.\n",
    "\n",
    "    skip 3: if the original hash value was $h$, then try $h+3$, then $h+6$, then $h+9$, then $h+12$ (wrap around the table as necessary)\n",
    "\n",
    "    Let's try skip 3 with 13, 56, 193, 84, 73, 264\n",
    "    \n",
    "    Problem: hash table size should be a prime number so that you eventually get to all slots in the table, still results in some clustering\n",
    "    \n",
    "    \n",
    "    \n",
    "* _quadratic probing_ - increase the skip value each time by the next square number\n",
    "    \n",
    "    if the original hash value was $h$, then try $h+1$, then $h+4$, then $h+9$, then $h+16$ (wrap around the table as necessary)\n",
    "    \n",
    "* _PRNG probing_ - use a pseudo-random number generator to generate the probing sequence (this is what Python uses for sets and dicionaries)\n",
    "    \n",
    "ALl of these techniques that try to find open slot somewhere else in the table are called __open addressing__\n",
    "    "
   ]
  },
  {
   "attachments": {
    "chaining.png": {
     "image/png": "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"
    }
   },
   "cell_type": "markdown",
   "id": "0629f972",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Chaining\n",
    "\n",
    "__Chaining:__ A collision resolution strategy in which values are placed into a linked list referenced by each spot in the hash table\n",
    "\n",
    "Eliminates clustering!\n",
    "\n",
    "<div>\n",
    "<img src=\"attachment:chaining.png\" />\n",
    "</div>\n",
    "\n",
    "Let's try chaining with 13, 56, 193, 84, 73, 264"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a369a617",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Hash Tables as a Data Structure\n",
    "\n",
    "\n",
    "Hash Tables \n",
    "* don't guarantee any particular ordering - it all depends on what the hash function does\n",
    "* __bad__ for ADTs that have order or indexes: `UnorderedList`, `OrderedList`, `Queue`, `Stack`, `Deque`\n",
    "* __good__ for ADTs where order doesn't matter: `Map` and `Set`\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb146d29",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Set ADT\n",
    "\n",
    "The above examples consider using hash tables as the underlying data structure to implement a __set__ abstract data type - similar to Python's `set` type.\n",
    "* A __set__ is a collection in which the ordering of the values is not important\n",
    "* The only thing that matters with a set is whether the item is in the set or not\n",
    "* Duplicates are not allowed\n",
    "* Python has a built-in set type\n",
    "\n",
    "The book does not formally define the Set ADT, but we could define it like this:\n",
    "* `Set()` Create a new, empty set. It returns an empty set collection.\n",
    "* `add(val)` Add a new value to the set. If the value is already in the set, do nothing.\n",
    "* `in` Return True for a statement of the form `val in set`, if the given value is in the set, False otherwise.\n",
    "* `remove(val)` Remove the given value from the set.\n",
    "* `len()` Return the number of values stored in the set.\n",
    "\n",
    "\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "dd1f13b8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'set'>\n",
      "{'butter', 'eggs', 'leftovers', 'milk'}\n",
      "True\n",
      "False\n"
     ]
    }
   ],
   "source": [
    "items_in_my_fridge = {\"milk\",\"eggs\",\"leftovers\",\"pop\"}\n",
    "items_in_my_fridge.add(\"butter\")\n",
    "items_in_my_fridge.add(\"butter\")\n",
    "items_in_my_fridge.remove(\"pop\")\n",
    "print( type(items_in_my_fridge) )\n",
    "print(items_in_my_fridge)\n",
    "print( \"butter\" in items_in_my_fridge )\n",
    "print( \"pop\" in items_in_my_fridge )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4b2875b4",
   "metadata": {},
   "source": [
    "## Implementing the Set ADT with a Chained Hash Table as the underlying data structure\n",
    "\n",
    "Coding demo"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "582001e9",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "3a702086",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0:[]\n",
      "1:[]\n",
      "2:[]\n",
      "3:[13, 193, 73]\n",
      "4:[84, 264]\n",
      "5:[]\n",
      "6:[56]\n",
      "7:[]\n",
      "8:[]\n",
      "9:[]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# Example code for your notes\n",
    "\n",
    "class ChainedHashSet:\n",
    "    \n",
    "    def __init__(self,table_size=10):\n",
    "        self.table = []\n",
    "        self.table_size = table_size\n",
    "        for slot in range(self.table_size):\n",
    "            self.table.append([])\n",
    "            \n",
    "    def __repr__(self):\n",
    "        display_str = \"\"\n",
    "        for slot in range(self.table_size):\n",
    "            display_str += str(slot)+\":\"+str(self.table[slot])+\"\\n\"\n",
    "        return display_str\n",
    "    \n",
    "    def hash_function(self,item):\n",
    "        return item%self.table_size\n",
    "    \n",
    "    def add(self,item):\n",
    "        hashed_val = self.hash_function(item)\n",
    "        list_at_slot = self.table[ hashed_val ]\n",
    "        if not item in list_at_slot:\n",
    "            list_at_slot.append(item)\n",
    "    \n",
    "    \n",
    "my_set = ChainedHashSet()\n",
    "\n",
    "my_set.add(13)\n",
    "my_set.add(56)\n",
    "my_set.add(193)\n",
    "my_set.add(84)\n",
    "my_set.add(73)\n",
    "my_set.add(264)\n",
    "\n",
    "\n",
    "print(my_set)        "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "15b6ab2c",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "## Group Activity Problem 3\n",
    "\n",
    "Implement the `__contains__` method - which will allow you to use the `in` operator to check if an item is in the set. \n",
    "\n",
    "_Note:_ it should return `True` or `False`\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d684528c",
   "metadata": {},
   "source": [
    "## Group Activity Problem 4\n",
    "\n",
    "Our hash table will still only store integers. \n",
    "\n",
    "Can you think of a strategy that will allow us to put strings in a table?\n",
    "\n",
    "```\n",
    "0:[]\n",
    "1:[]\n",
    "2:[]\n",
    "3:['Dr. Mario']\n",
    "4:[]\n",
    "5:['Pikachu', 'Kirby']\n",
    "6:['Sheik', 'Mr. Game and Watch']\n",
    "7:['Dr. Carlson', 'Jiggly Puff']\n",
    "8:['Captain Falcon']\n",
    "9:['Mew Two']\n",
    "```\n",
    "\n",
    "\n",
    "_these are the names my kids gave our chicks that just hatched_"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1a29b39d",
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "id": "7ba72034",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Map ADT\n",
    "\n",
    "Hash tables are also often used to implement the __map__ abstract data type\n",
    "\n",
    "A __map__ abstract data type stores _key-value_ pairs and allows you to use a _key_ to look up its associated _value_.\n",
    "\n",
    "A Python dictionary is a map.\n",
    "\n",
    "There are other data structures you could use to implement a map, such as a list of tuples.\n",
    "\n",
    "The following is the book's definition of the Map ADT:\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bac37f0b",
   "metadata": {},
   "source": [
    "* `Map()` Create a new, empty map. It returns an empty map collection.\n",
    "* `put(key,val)` Add a new key-value pair to the map. If the key is already in the map then replace the old value with the new value.\n",
    "* `get(key)` Given a key, return the value stored in the map or `None` otherwise.\n",
    "* `del` Delete the key-value pair from the map using a statement of the form `del map[key]`.\n",
    "* `len()` Return the number of key-value pairs stored in the map.\n",
    "* `in` Return True for a statement of the form key in map, if the given key is in the map, False otherwise.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "53041395",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "## Chained Hash Map\n",
    "\n",
    "We could use a similar strategy that we used for set, but store _(key,value)_ tuples\n",
    "\n",
    "```\n",
    "0:[(20, 'chicken')]\n",
    "1:[(31, 'cow')]\n",
    "2:[]\n",
    "3:[(93, 'lion')]\n",
    "4:[(54, 'cat'), (44, 'goat')]\n",
    "5:[(55, 'pig')]\n",
    "6:[(26, 'dog')]\n",
    "7:[(17, 'tiger'), (77, 'bird')]\n",
    "8:[]\n",
    "9:[]\n",
    "```\n",
    "\n",
    "(maybe this is a map that a zoo uses to look up which animal has each id)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3a0ea162",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "## Group Activity Problem 5\n",
    "\n",
    "With our set, we did something like this"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a6947dd8",
   "metadata": {},
   "outputs": [],
   "source": [
    "    def add(self,item):\n",
    "        hashed_val = self.hash_function(item)\n",
    "        list_at_slot = self.table[ hashed_val ]\n",
    "        if not item in list_at_slot:\n",
    "            list_at_slot.append(item)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "80276654",
   "metadata": {},
   "source": [
    "For a map, it would look like this"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9a08a8e3",
   "metadata": {},
   "outputs": [],
   "source": [
    "    def put(self,key,value):\n",
    "        hashed_key = self.hash_function(key)\n",
    "        list_at_slot = self.table[ hashed_key ]\n",
    "        if not (key,value) in list_at_slot:\n",
    "            list_at_slot.append((key,value))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "382c5589",
   "metadata": {},
   "source": [
    "What would happend if you tried to change the value associate with a key?\n",
    "\n",
    "How can you fix it?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ea750674",
   "metadata": {},
   "outputs": [],
   "source": [
    "my_map = ChainedHashMap()\n",
    "\n",
    "my_map.put(20,\"Turkey\")\n",
    "my_map.put(20,\"Chicken\") #should overwrite Turkey"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0059d978",
   "metadata": {},
   "source": [
    "## Built-in Hash Function\n",
    "\n",
    "Python contains a built-in hash function that you can use.\n",
    "\n",
    "Be careful, the hash is somewhat randomized and values change every time you re-start your code (this brings issues with saving hashed values to a file, etc.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "ba82f601",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1538015278275964211\n"
     ]
    }
   ],
   "source": [
    "print( hash(\"Star Wars: Episode VII - The Force Awakens (2015)\") )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1135be9b",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Textbook's Linear-Probed-Hash-Table Map\n",
    "\n",
    "The following code shows the start of the book's approach to using a Hash Table with linear probing to implement a map.\n",
    "\n",
    "`self.slots` list stores the keys\n",
    "\n",
    "`self.data` stores the associated values\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "695dff28",
   "metadata": {},
   "outputs": [],
   "source": [
    "class HashTable:\n",
    "    def __init__(self):\n",
    "        self.size = 11\n",
    "        self.slots = [None] * self.size\n",
    "        self.data = [None] * self.size\n",
    "        \n",
    "    def put(self,key,data):\n",
    "        hashvalue = self.hashfunction(key,len(self.slots))\n",
    "\n",
    "        if self.slots[hashvalue] == None:\n",
    "            self.slots[hashvalue] = key\n",
    "            self.data[hashvalue] = data\n",
    "        else:\n",
    "            if self.slots[hashvalue] == key:\n",
    "                self.data[hashvalue] = data  #replace\n",
    "            else:\n",
    "                nextslot = self.rehash(hashvalue,len(self.slots))\n",
    "            while self.slots[nextslot] != None and self.slots[nextslot] != key:\n",
    "                nextslot = self.rehash(nextslot,len(self.slots))\n",
    "\n",
    "            if self.slots[nextslot] == None:\n",
    "                self.slots[nextslot]=key\n",
    "                self.data[nextslot]=data\n",
    "            else:\n",
    "                self.data[nextslot] = data #replace\n",
    "\n",
    "    def hashfunction(self,key,size):\n",
    "         return key%size\n",
    "\n",
    "    def rehash(self,oldhash,size):\n",
    "        return (oldhash+1)%size"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Slideshow",
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
