diff --git a/animations/hash_tables/index.html b/animations/hash_tables/index.html new file mode 100644 index 0000000..fb46268 --- /dev/null +++ b/animations/hash_tables/index.html @@ -0,0 +1,18 @@ + + + + + Separate Chaining Based Hash Table + + + + +

Separate Chaining Based Hash Table

+

This is the animation of Separate Chaining Based Hash Table. Click the "next step" button to run the animation.

+ + + +
+ + + diff --git a/animations/hash_tables/main.js b/animations/hash_tables/main.js new file mode 100644 index 0000000..ee35b9e --- /dev/null +++ b/animations/hash_tables/main.js @@ -0,0 +1,659 @@ +// Author: Xi Chen +// first we need to create a stage +var stage = new Konva.Stage({ + container: 'container', // id of container
+ width: 2000, + height: 1000 +}); + +// then create layer +var layer = new Konva.Layer(); + +function makeMemory(xstart, ystart, bufferSize, w, h) +{ + for (let i = 0; i < bufferSize; ++i) + { + let tr = new Konva.Rect + ({ + x: xstart, + y: ystart+i*h, + id:"brec"+i, + stroke: '#343434', + strokeWidth: 5, + fill: '#ddd', + width: w, + height: h, + shadowColor: 'black', + shadowBlur: 10, + shadowOffsetX: 10, + shadowOffsetY: 10, + shadowOpacity: 0.2, + }); + + layer.add(tr); + + // Add index number to the left of each box + let indexText = new Konva.Text + ({ + x: xstart - 30, // Position to the left of the box + y: ystart + i*h + h/2 - 10, // Center vertically + text: i.toString(), + fontSize: 18, + fontFamily: 'Calibri', + fill: '#000000', + align: 'center', + width: 30, + }); + + layer.add(indexText); + } // end of for loop +} + +makeMemory(1650,60,10,50,50); + +var memoryText = new Konva.Text +({ + x: 1625, + y: 25, + id:"memory", + text: "Memory", + fontSize: 28, + fontFamily: 'Calibri', + fill: '#000000', +}); + +var mainLabel = new Konva.Text +({ + x: 400, + y: 0, + id:"main", + text: "Main", + fontSize: 28, + fontFamily: 'Calibri', + fill: '#000000', +}); + +// box for code +layer.add(memoryText); +layer.add(mainLabel); + +// add the layer to the stage +stage.add(layer); + +// draw the image +layer.draw(); + +const steps = +[ + // Initialize phonebook + {mainHighlight: [1], funcHighlight: [], action: null, console: "Initializing phonebook array with size 10"}, + + // Add dan + {mainHighlight: [2], funcHighlight: [], action: null, console: "Adding entry: dan (5182764321)"}, + {mainHighlight: [2], funcHighlight: [13], action: null, console: "Entering add function"}, + {mainHighlight: [2], funcHighlight: [14], action: null, console: "Calculating hash index..."}, + {mainHighlight: [2], funcHighlight: [10], action: null, console: "Entering hash_function"}, + {mainHighlight: [2], funcHighlight: [11], action: null, console: "5182764321 % 10 = 1"}, + {mainHighlight: [2], funcHighlight: [14], action: null, console: "Hash index: 1"}, + {mainHighlight: [2], funcHighlight: [15], action: null, console: "Creating new node"}, + {mainHighlight: [2], funcHighlight: [16], action: null, console: "Setting name: dan"}, + {mainHighlight: [2], funcHighlight: [17], action: null, console: "Setting number: 5182764321"}, + {mainHighlight: [2], funcHighlight: [18], action: null, console: "Setting next pointer"}, + {mainHighlight: [2], funcHighlight: [19], action: {type: 'add', index: 1, name: 'dan', number: '5182764321'}, console: "Adding node to index 1"}, + {mainHighlight: [2], funcHighlight: [20], action: null, console: "Returning from add function"}, + + // Add fred + {mainHighlight: [3], funcHighlight: [], action: null, console: "Adding entry: fred (6173551212)"}, + {mainHighlight: [3], funcHighlight: [13], action: null, console: "Entering add function"}, + {mainHighlight: [3], funcHighlight: [14], action: null, console: "Calculating hash index..."}, + {mainHighlight: [3], funcHighlight: [10], action: null, console: "Entering hash_function"}, + {mainHighlight: [3], funcHighlight: [11], action: null, console: "6173551212 % 10 = 2"}, + {mainHighlight: [3], funcHighlight: [14], action: null, console: "Hash index: 2"}, + {mainHighlight: [3], funcHighlight: [15], action: null, console: "Creating new node"}, + {mainHighlight: [3], funcHighlight: [16], action: null, console: "Setting name: fred"}, + {mainHighlight: [3], funcHighlight: [17], action: null, console: "Setting number: 6173551212"}, + {mainHighlight: [3], funcHighlight: [18], action: null, console: "Setting next pointer"}, + {mainHighlight: [3], funcHighlight: [19], action: {type: 'add', index: 2, name: 'fred', number: '6173551212'}, console: "Adding node to index 2"}, + {mainHighlight: [3], funcHighlight: [20], action: null, console: "Returning from add function"}, + + // Add alice + {mainHighlight: [4], funcHighlight: [], action: null, console: "Adding entry: alice (5182761234)"}, + {mainHighlight: [4], funcHighlight: [13], action: null, console: "Entering add function"}, + {mainHighlight: [4], funcHighlight: [14], action: null, console: "Calculating hash index..."}, + {mainHighlight: [4], funcHighlight: [10], action: null, console: "Entering hash_function"}, + {mainHighlight: [4], funcHighlight: [11], action: null, console: "5182761234 % 10 = 4"}, + {mainHighlight: [4], funcHighlight: [14], action: null, console: "Hash index: 4"}, + {mainHighlight: [4], funcHighlight: [15], action: null, console: "Creating new node"}, + {mainHighlight: [4], funcHighlight: [16], action: null, console: "Setting name: alice"}, + {mainHighlight: [4], funcHighlight: [17], action: null, console: "Setting number: 5182761234"}, + {mainHighlight: [4], funcHighlight: [18], action: null, console: "Setting next pointer"}, + {mainHighlight: [4], funcHighlight: [19], action: {type: 'add', index: 4, name: 'alice', number: '5182761234'}, console: "Adding node to index 4"}, + {mainHighlight: [4], funcHighlight: [20], action: null, console: "Returning from add function"}, + + // Add carol + {mainHighlight: [5], funcHighlight: [], action: null, console: "Adding entry: carol (5182761267)"}, + {mainHighlight: [5], funcHighlight: [13], action: null, console: "Entering add function"}, + {mainHighlight: [5], funcHighlight: [14], action: null, console: "Calculating hash index..."}, + {mainHighlight: [5], funcHighlight: [10], action: null, console: "Entering hash_function"}, + {mainHighlight: [5], funcHighlight: [11], action: null, console: "5182761267 % 10 = 7"}, + {mainHighlight: [5], funcHighlight: [14], action: null, console: "Hash index: 7"}, + {mainHighlight: [5], funcHighlight: [15], action: null, console: "Creating new node"}, + {mainHighlight: [5], funcHighlight: [16], action: null, console: "Setting name: carol"}, + {mainHighlight: [5], funcHighlight: [17], action: null, console: "Setting number: 5182761267"}, + {mainHighlight: [5], funcHighlight: [18], action: null, console: "Setting next pointer"}, + {mainHighlight: [5], funcHighlight: [19], action: {type: 'add', index: 7, name: 'carol', number: '5182761267'}, console: "Adding node to index 7"}, + {mainHighlight: [5], funcHighlight: [20], action: null, console: "Returning from add function"}, + + // Add bob + {mainHighlight: [6], funcHighlight: [], action: null, console: "Adding entry: bob (5182765678)"}, + {mainHighlight: [6], funcHighlight: [13], action: null, console: "Entering add function"}, + {mainHighlight: [6], funcHighlight: [14], action: null, console: "Calculating hash index..."}, + {mainHighlight: [6], funcHighlight: [10], action: null, console: "Entering hash_function"}, + {mainHighlight: [6], funcHighlight: [11], action: null, console: "5182765678 % 10 = 8"}, + {mainHighlight: [6], funcHighlight: [14], action: null, console: "Hash index: 8"}, + {mainHighlight: [6], funcHighlight: [15], action: null, console: "Creating new node"}, + {mainHighlight: [6], funcHighlight: [16], action: null, console: "Setting name: bob"}, + {mainHighlight: [6], funcHighlight: [17], action: null, console: "Setting number: 5182765678"}, + {mainHighlight: [6], funcHighlight: [18], action: null, console: "Setting next pointer"}, + {mainHighlight: [6], funcHighlight: [19], action: {type: 'add', index: 8, name: 'bob', number: '5182765678'}, console: "Adding node to index 8"}, + {mainHighlight: [6], funcHighlight: [20], action: null, console: "Returning from add function"}, + + // Add erin + {mainHighlight: [7], funcHighlight: [], action: null, console: "Adding entry: erin (5182764488)"}, + {mainHighlight: [7], funcHighlight: [13], action: null, console: "Entering add function"}, + {mainHighlight: [7], funcHighlight: [14], action: null, console: "Calculating hash index..."}, + {mainHighlight: [7], funcHighlight: [10], action: null, console: "Entering hash_function"}, + {mainHighlight: [7], funcHighlight: [11], action: null, console: "5182764488 % 10 = 8"}, + {mainHighlight: [7], funcHighlight: [14], action: null, console: "Hash index: 8"}, + {mainHighlight: [7], funcHighlight: [15], action: null, console: "Creating new node"}, + {mainHighlight: [7], funcHighlight: [16], action: null, console: "Setting name: erin"}, + {mainHighlight: [7], funcHighlight: [17], action: null, console: "Setting number: 5182764488"}, + {mainHighlight: [7], funcHighlight: [18], action: null, console: "Setting next pointer"}, + {mainHighlight: [7], funcHighlight: [19], action: {type: 'add', index: 8, name: 'erin', number: '5182764488', chain: true}, console: "Adding node to index 8 (chaining)"}, + {mainHighlight: [7], funcHighlight: [20], action: null, console: "Returning from add function"}, + + // Identify dan + {mainHighlight: [8], funcHighlight: [], action: null, console: "Searching for number: 5182764321"}, + {mainHighlight: [8], funcHighlight: [21], action: null, console: "Entering identify function"}, + {mainHighlight: [8], funcHighlight: [22], action: null, console: "Calculating hash index..."}, + {mainHighlight: [8], funcHighlight: [10], action: null, console: "Entering hash_function"}, + {mainHighlight: [8], funcHighlight: [11], action: null, console: "5182764321 % 10 = 1"}, + {mainHighlight: [8], funcHighlight: [22], action: null, console: "Hash index: 1"}, + {mainHighlight: [8], funcHighlight: [23], action: null, console: "Starting search at index 1"}, + {mainHighlight: [8], funcHighlight: [24], action: null, console: "phonebook[1] is not nullptr(true)"}, + {mainHighlight: [8], funcHighlight: [25], action: null, console: "5182764321 == 5182764321(true)"}, + {mainHighlight: [8], funcHighlight: [26], action: null, console: "Returning: dan"}, + {mainHighlight: [8], funcHighlight: [31], action: null, console: "Returning from identify function"}, + {mainHighlight: [8], funcHighlight: [], action: null, console: "Identify 5182764321: dan"}, + + // Identify erin + {mainHighlight: [9], funcHighlight: [], action: null, console: "Searching for number: 5182764488"}, + {mainHighlight: [9], funcHighlight: [21], action: null, console: "Entering identify function"}, + {mainHighlight: [9], funcHighlight: [22], action: null, console: "Calculating hash index..."}, + {mainHighlight: [9], funcHighlight: [10], action: null, console: "Entering hash_function"}, + {mainHighlight: [9], funcHighlight: [11], action: null, console: "5182764488 % 10 = 8"}, + {mainHighlight: [9], funcHighlight: [22], action: null, console: "Hash index: 8"}, + {mainHighlight: [9], funcHighlight: [23], action: null, console: "Starting search at index 8"}, + {mainHighlight: [9], funcHighlight: [24], action: null, console: "phonebook[8] is not nullptr(true)"}, + {mainHighlight: [9], funcHighlight: [25], action: null, console: "5182765678 != 5182764488(false)"}, + {mainHighlight: [9], funcHighlight: [28], action: null, console: "Moving to next node"}, + {mainHighlight: [9], funcHighlight: [24], action: null, console: "phonebook[8]->next is not nullptr(true)"}, + {mainHighlight: [9], funcHighlight: [25], action: null, console: "5182764488 == 5182764488(true)"}, + {mainHighlight: [9], funcHighlight: [26], action: null, console: "Returning: erin"}, + {mainHighlight: [9], funcHighlight: [31], action: null, console: "Returning from identify function"}, + {mainHighlight: [9], funcHighlight: [], action: null, console: "Identify 5182764488: erin"}, + + // Identify fred + {mainHighlight: [10], funcHighlight: [], action: null, console: "Searching for number: 6173551212"}, + {mainHighlight: [10], funcHighlight: [21], action: null, console: "Entering identify function"}, + {mainHighlight: [10], funcHighlight: [22], action: null, console: "Calculating hash index..."}, + {mainHighlight: [10], funcHighlight: [10], action: null, console: "Entering hash_function"}, + {mainHighlight: [10], funcHighlight: [11], action: null, console: "6173551212 % 10 = 2"}, + {mainHighlight: [10], funcHighlight: [22], action: null, console: "Hash index: 2"}, + {mainHighlight: [10], funcHighlight: [23], action: null, console: "Starting search at index 2"}, + {mainHighlight: [10], funcHighlight: [24], action: null, console: "phonebook[2] is not nullptr(true)"}, + {mainHighlight: [10], funcHighlight: [25], action: null, console: "6173551212 == 6173551212(true)"}, + {mainHighlight: [10], funcHighlight: [26], action: null, console: "Returning: fred"}, + {mainHighlight: [10], funcHighlight: [31], action: null, console: "Returning from identify function"}, + {mainHighlight: [10], funcHighlight: [], action: null, console: "Identify 6173551212: fred"}, + + // Identify not found + {mainHighlight: [11], funcHighlight: [], action: null, console: "Searching for number: 1234567890"}, + {mainHighlight: [11], funcHighlight: [21], action: null, console: "Entering identify function"}, + {mainHighlight: [11], funcHighlight: [22], action: null, console: "Calculating hash index..."}, + {mainHighlight: [11], funcHighlight: [10], action: null, console: "Entering hash_function"}, + {mainHighlight: [11], funcHighlight: [11], action: null, console: "1234567890 % 10 = 0"}, + {mainHighlight: [11], funcHighlight: [22], action: null, console: "Hash index: 0"}, + {mainHighlight: [11], funcHighlight: [23], action: null, console: "Starting search at index 0"}, + {mainHighlight: [11], funcHighlight: [24], action: null, console: "phonebook[0] is not nullptr(false)"}, + {mainHighlight: [11], funcHighlight: [30], action: null, console: "Returning: Not found"}, + {mainHighlight: [11], funcHighlight: [31], action: null, console: "Returning from identify function"}, + {mainHighlight: [11], funcHighlight: [], action: null, console: "Identify 1234567890: Not found"}, + + // Return 0 + {mainHighlight: [12], funcHighlight: [], action: null, console: "Program completed"} +]; + +function highlightCodeLines(mainLines, funcLines) +{ + for (let i = 0; i < mainCodeTextNodes.length; ++i) + { + if (mainLines.includes(i)) + { + mainCodeTextNodes[i].fontStyle('normal'); + mainCodeTextNodes[i].fill('#FF0000'); // Bright red for current line + } + else + { + mainCodeTextNodes[i].fontStyle('normal'); + mainCodeTextNodes[i].fill('#222'); + } + } + + for (let i = 0; i < funcCodeTextNodes.length; ++i) + { + if (funcLines.includes(i)) + { + funcCodeTextNodes[i].fontStyle('normal'); + funcCodeTextNodes[i].fill('#FF0000'); // Bright red for current line + } + else + { + funcCodeTextNodes[i].fontStyle('normal'); + funcCodeTextNodes[i].fill('#222'); + } + } + + layer.draw(); +} + +let stepIndex = 0; + +function nextstep() +{ + if (stepIndex >= steps.length) + { + alert('End of animation! Refresh to restart.'); + return; + } + + let step = steps[stepIndex]; + highlightCodeLines(step.mainHighlight, step.funcHighlight); + + // Perform the action first + if (step.action && step.action.type === 'add') + { + addNodeToMemory(step.action.index, step.action.name, step.action.number, step.action.chain); + // Then show the console message + if (step.console) + { + addConsoleText(step.console); + } + + layer.draw(); + ++stepIndex; + + } + else if (step.action && step.action.type === 'identify') + { + highlightIdentifyPath(step.action.index, step.action.path); + } + else + { + // No action, just show console message + if (step.console) + { + addConsoleText(step.console); + } + + layer.draw(); + ++stepIndex; + } +} + +window.nextstep = nextstep; + +function highlightNext(index, path, i) +{ + if (i < path.length) + { + let obj = memoryNodes[index][path[i]]; + + if (obj) + { + obj.group.children.each(child => { + if (child instanceof Konva.Rect) + { + child.stroke('#FF0000'); // red for current + child.strokeWidth(5); + } + }); + } + + layer.draw(); + ++i; + + if (i < path.length) + { + setTimeout(() => highlightNext(index, path, i), 1000); + } + } +} + +function highlightIdentifyPath(index, path) +{ + // Remove previous highlights + for (let idx in memoryNodes) + { + memoryNodes[idx].forEach(obj => { + obj.group.children.each(child => { + if (child instanceof Konva.Rect) + { + child.stroke('#222'); + child.strokeWidth(2); + } + }); + }); + } + + if (!memoryNodes[index]) return; + + // Start the animation + highlightNext(index, path, 0); +} + +// --- Main Code Box (left) --- +const mainCodeLines = +[ + 'int main(){', + ' Node *phonebook[PHONEBOOK_SIZE] = {nullptr};', + ' add(phonebook, 5182764321, "dan");', + ' add(phonebook, 6173551212, "fred");', + ' add(phonebook, 5182761234, "alice");', + ' add(phonebook, 5182761267, "carol");', + ' add(phonebook, 5182765678, "bob");', + ' add(phonebook, 5182764488, "erin");', + ' std::cout << "Identify 5182764321: " << identify(phonebook, 5182764321) << std::endl;', + ' std::cout << "Identify 5182764488: " << identify(phonebook, 5182764488) << std::endl;', + ' std::cout << "Identify 6173551212: " << identify(phonebook, 6173551212) << std::endl;', + ' std::cout << "Identify 1234567890: " << identify(phonebook, 1234567890) << std::endl;', + ' return 0;', + '}' +]; + +// --- Function Code Box (center) --- +const funcCodeLines = +[ + '#include ', + '#include ', + 'const int PHONEBOOK_SIZE = 10;', + 'struct Node{', + 'public:', + ' int number;', + ' std::string name;', + ' Node *next;', + ' Node() : name(""), number(0), next(nullptr) {}', + '};', + 'int hash_function(int number){', + ' return number % PHONEBOOK_SIZE;', + '}', + 'void add(Node *phonebook[PHONEBOOK_SIZE], int number, const std::string &name){', + ' int index = hash_function(number);', + ' Node *tmp = new Node;', + ' tmp->name = name;', + ' tmp->number = number;', + ' tmp->next = phonebook[index];', + ' phonebook[index] = tmp;', + '}', + 'std::string identify(Node *phonebook[PHONEBOOK_SIZE], int number){', + ' int index = hash_function(number);', + ' Node *curr = phonebook[index];', + ' while (curr != nullptr){', + ' if (curr->number == number){', + ' return curr->name;', + ' }', + ' curr = curr->next;', + ' }', + ' return "Not found";', + '}' +]; + +// Draw main code box (left) +var mainCodeBox = new Konva.Rect +({ + x: 20, + y: 25, + width: 800, + height: mainCodeLines.length * 24 + 40, + fill: '#ddd', + stroke: '#555', + strokeWidth: 5, + shadowColor: 'black', + shadowBlur: 10, + shadowOffsetX: 10, + shadowOffsetY: 10, + shadowOpacity: 0.2, + cornerRadius: 10, +}); + +layer.add(mainCodeBox); + +// Draw console box under main code box +var consoleBox = new Konva.Rect +({ + x: 20, + y: mainCodeBox.y() + mainCodeBox.height() + 40, + width: 800, + height: 100, + fill: '#ddd', + stroke: '#555', + strokeWidth: 5, + shadowColor: 'black', + shadowBlur: 10, + shadowOffsetX: 10, + shadowOffsetY: 10, + shadowOpacity: 0.2, + cornerRadius: 10, +}); + +layer.add(consoleBox); + +var consoleLabel = new Konva.Text +({ + x: 375, + y: consoleBox.y() - 25, + id: 'console', + text: 'Console', + fontSize: 28, + fontFamily: 'Calibri', + fill: '#000000', +}); + +layer.add(consoleLabel); + +let consoleTextNode = null; + +function addConsoleText(text) +{ + if (consoleTextNode) + { + consoleTextNode.destroy(); + } + + consoleTextNode = new Konva.Text + ({ + x: 30, + y: consoleBox.y() + 30, + text: text, + id: 'console_line', + fontSize: 16, + fontFamily: 'Consolas', + fill: '#000000', + width: 785, + padding: 1, + wrap: 'none', + }); + + layer.add(consoleTextNode); + layer.draw(); +} + +let mainCodeTextNodes = []; + +for (let i = 0; i < mainCodeLines.length; ++i) +{ + let t = new Konva.Text + ({ + x: 30, + y: 40 + i * 24, + text: mainCodeLines[i], + id: 'main_code_line_' + i, + fontSize: 16, + fontFamily: 'Consolas', + fill: '#222', + width: 785, + padding: 1, + wrap: 'none', + }); + + mainCodeTextNodes.push(t); + layer.add(t); +} + +// Function code box (center) +var funcCodeBox = new Konva.Rect +({ + x: 850, + y: 25, + width: 750, + height: funcCodeLines.length * 24 + 30, + fill: '#ddd', + stroke: '#555', + strokeWidth: 5, + shadowColor: 'black', + shadowBlur: 10, + shadowOffsetX: 10, + shadowOffsetY: 10, + shadowOpacity: 0.2, + cornerRadius: 10, +}); + +layer.add(funcCodeBox); + +var funcCodeLabel = new Konva.Text +({ + x: 1175, + y: 0, + id: 'func_code', + text: 'Functions', + fontSize: 28, + fontFamily: 'Calibri', + fill: '#000000', +}); + +layer.add(funcCodeLabel); + +let funcCodeTextNodes = []; + +for (let i = 0; i < funcCodeLines.length; ++i) +{ + let t = new Konva.Text + ({ + x: 875, + y: 40 + i * 24, + text: funcCodeLines[i], + id: 'func_code_line_' + i, + fontSize: 16, + fontFamily: 'Consolas', + fill: '#222', + width: 750, + padding: 1, + wrap: 'none', + }); + + funcCodeTextNodes.push(t); + layer.add(t); +} + +let memoryNodes = {}; + +function addNodeToMemory(index, name, number, chain = false) +{ + if (!memoryNodes[index]) memoryNodes[index] = []; + let y = 60 + index * 50 + 10; + let x = 1725 + memoryNodes[index].length * 150; + + // Draw node group (rectangle + text) + let group = new Konva.Group(); + + let nodeRect = new Konva.Rect + ({ + x: x, + y: y, + width: 100, + height: 30, + fill: '#fff', + stroke: '#222', + strokeWidth: 2, + cornerRadius: 5, + }); + + let numberText = new Konva.Text + ({ + x: x + 5, + y: y + 2, + text: number, + fontSize: 12, + fontFamily: 'Consolas', + fill: '#333', + }); + + let nameText = new Konva.Text + ({ + x: x + 5, + y: y + 15, + text: name, + fontSize: 15, + fontFamily: 'Calibri', + fill: '#000', + fontStyle: 'bold', + }); + + group.add(nodeRect); + group.add(numberText); + group.add(nameText); + layer.add(group); + + // Draw arrow from memory box or previous node + let arrow; + + if (memoryNodes[index].length === 0) + { + arrow = new Konva.Arrow + ({ + points: [1700, y + 15, x, y + 15], + pointerLength: 10, + pointerWidth: 10, + fill: 'black', + stroke: 'black', + strokeWidth: 2, + }); + } + else + { + let prevX = 1725 + (memoryNodes[index].length - 1) * 150 + 100; + + arrow = new Konva.Arrow + ({ + points: [prevX, y + 15, x, y + 15], + pointerLength: 10, + pointerWidth: 10, + fill: 'black', + stroke: 'black', + strokeWidth: 2, + }); + } + + layer.add(arrow); + memoryNodes[index].push({group, arrow}); +} diff --git a/lectures/22_hash_tables_I/README.md b/lectures/22_hash_tables_I/README.md index 56a8290..0426f6c 100644 --- a/lectures/22_hash_tables_I/README.md +++ b/lectures/22_hash_tables_I/README.md @@ -138,6 +138,8 @@ std::string identify(Node* phonebook[PHONEBOOK_SIZE], int number) { } ``` +Play this [animation](https://jidongxiao.github.io/CSCI1200-DataStructures/animations/hash_tables/index.html) to understand how this works. + ## 22.7 Exercise: Hash Table Performance - What's the memory complexity for the hash-table-based Caller ID system?