Compare commits
34 Commits
903e76ef1f
...
04af78b253
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04af78b253 | ||
|
|
dc7c973256 | ||
|
|
c9bd945e27 | ||
|
|
aaecda717c | ||
|
|
3f47154335 | ||
|
|
94e66529f4 | ||
|
|
af2c2bddd2 | ||
|
|
cf9b51d64a | ||
|
|
c34d107f78 | ||
|
|
bb7486a01b | ||
|
|
890d4d2be8 | ||
|
|
95d9bb50ca | ||
|
|
186a7e60ea | ||
|
|
a7239d00a9 | ||
|
|
d2d1a34eec | ||
|
|
23ac44932a | ||
|
|
4a88689106 | ||
|
|
c26e4f8fc3 | ||
|
|
3d5614564f | ||
|
|
07f53dedfe | ||
|
|
67c5cb8641 | ||
|
|
28d01974f1 | ||
|
|
281c47d771 | ||
|
|
df38b3cf36 | ||
|
|
79e526004b | ||
|
|
0be3f92396 | ||
|
|
d8f573c722 | ||
|
|
6929005253 | ||
|
|
086aad56cf | ||
|
|
23acdc2bea | ||
|
|
a37e7408b7 | ||
|
|
4df57b18b3 | ||
|
|
d7dd0f1e80 | ||
|
|
da64e34f47 |
22
animations/stack/index.html
Normal file
22
animations/stack/index.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Valid Parentheses</title>
|
||||
<script src="../konva.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h1>Valid Parentheses</h1>
|
||||
<p>This animation shows if parentheses entered are valid using stack</p>
|
||||
|
||||
<button onclick="case1()">Case 1: "()"</button>
|
||||
<button onclick="case2()">Case 2: "()[]{}"</button>
|
||||
<button onclick="case3()">Case 3: "(]"</button>
|
||||
<button onclick="case4()">Case 4: "([])"</button>
|
||||
<button onclick="case5()">Case 5: "{[([])]}"</button>
|
||||
|
||||
<div id="container"></div>
|
||||
<script src="stack.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
605
animations/stack/stack.js
Normal file
605
animations/stack/stack.js
Normal file
@@ -0,0 +1,605 @@
|
||||
// Author: Mykolas Ruth
|
||||
// Create a stage for the animation
|
||||
var stage = new Konva.Stage({
|
||||
container: 'container',
|
||||
width: 1200,
|
||||
height: 700
|
||||
});
|
||||
|
||||
// Create layer
|
||||
var layer = new Konva.Layer();
|
||||
|
||||
// Variables to track animation state
|
||||
var stack = [];
|
||||
var lastPopped = "";
|
||||
var currentString = "";
|
||||
var currentIndex = 0;
|
||||
var pc = 0; // Program counter for animation steps
|
||||
var isComplete = false;
|
||||
|
||||
// Stack size and positioning
|
||||
var stack_size = 10;
|
||||
var stack_x = 1000;
|
||||
var stack_y = 100;
|
||||
var stack_width = 120;
|
||||
var stack_height = 40;
|
||||
|
||||
// Initialize the visualization
|
||||
function init() {
|
||||
// Reset layer
|
||||
layer.destroyChildren();
|
||||
|
||||
// Create code display
|
||||
createCodeDisplay();
|
||||
|
||||
// Create stack visualization
|
||||
createStack();
|
||||
|
||||
// Create animation controls area
|
||||
createNextStepButton();
|
||||
|
||||
// Add the layer to the stage
|
||||
stage.add(layer);
|
||||
layer.draw();
|
||||
}
|
||||
|
||||
// Create the code display
|
||||
function createCodeDisplay() {
|
||||
var codeRect = new Konva.Rect({
|
||||
x: 50,
|
||||
y: 50,
|
||||
width: 450,
|
||||
height: 600,
|
||||
stroke: '#555',
|
||||
strokeWidth: 2,
|
||||
fill: '#f5f5f5',
|
||||
shadowColor: 'black',
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 5,
|
||||
shadowOffsetY: 5,
|
||||
shadowOpacity: 0.2,
|
||||
cornerRadius: 10,
|
||||
id: 'code_rect'
|
||||
});
|
||||
layer.add(codeRect);
|
||||
|
||||
// Add the code text
|
||||
var codeLines = [
|
||||
"bool isValid(string s) {",
|
||||
" std::stack<char> myStack;",
|
||||
" int len = s.length();",
|
||||
" char c;",
|
||||
" for(int i=0; i<len; i++) {",
|
||||
" // Push opening brackets to stack",
|
||||
" if(s[i]=='(' || s[i]=='{' || s[i]=='[') {",
|
||||
" myStack.push(s[i]);",
|
||||
" } else {",
|
||||
" // Check for closing brackets",
|
||||
" if(myStack.empty()) {",
|
||||
" return false;",
|
||||
" }",
|
||||
" c = myStack.top();",
|
||||
" myStack.pop();",
|
||||
" // Check bracket matching",
|
||||
" if(s[i]==')' && c!='(') return false;",
|
||||
" if(s[i]=='}' && c!='{') return false;",
|
||||
" if(s[i]==']' && c!='[') return false;",
|
||||
" }",
|
||||
" }",
|
||||
" // Check if all brackets are matched",
|
||||
" return myStack.empty();",
|
||||
"}"
|
||||
];
|
||||
|
||||
for (var i = 0; i < codeLines.length; i++) {
|
||||
makeText(60, 60 + i * 20, codeLines[i], 'code_line_' + i);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the stack boxes
|
||||
function createStack() {
|
||||
makeText(stack_x, stack_y - 50, "Stack", "stack_title");
|
||||
|
||||
// rectangles on stack
|
||||
for (var i = 0; i < stack_size; i++) {
|
||||
var rect = new Konva.Rect({
|
||||
x: stack_x,
|
||||
y: stack_y + i * stack_height,
|
||||
id: "stack_rec" + i,
|
||||
stroke: '#343434',
|
||||
strokeWidth: 2,
|
||||
fill: '#f0f0f0',
|
||||
width: stack_width,
|
||||
height: stack_height,
|
||||
});
|
||||
layer.add(rect);
|
||||
}
|
||||
|
||||
var base = new Konva.Rect({
|
||||
x: stack_x,
|
||||
y: stack_y + stack_size * stack_height,
|
||||
width: stack_width,
|
||||
height: 5,
|
||||
fill: 'black',
|
||||
});
|
||||
layer.add(base);
|
||||
}
|
||||
|
||||
// create next step button
|
||||
function createNextStepButton() {
|
||||
|
||||
var nextStepButton = new Konva.Label({
|
||||
x: 530,
|
||||
y: 400,
|
||||
|
||||
});
|
||||
|
||||
nextStepButton.add(new Konva.Tag({
|
||||
fill: 'blue',
|
||||
cornerRadius: 3,
|
||||
shadowColor: 'black',
|
||||
}));
|
||||
|
||||
nextStepButton.add(new Konva.Text({
|
||||
text: 'Next Step',
|
||||
fontSize: 18,
|
||||
padding: 10,
|
||||
fill: 'white'
|
||||
}));
|
||||
|
||||
nextStepButton.on('click', function() {
|
||||
nextStep();
|
||||
});
|
||||
|
||||
|
||||
layer.add(nextStepButton);
|
||||
}
|
||||
|
||||
// Create the input string display
|
||||
function createInputDisplay(str) {
|
||||
// Clear previous displays
|
||||
var prevDisplay = stage.find('#input_display');
|
||||
prevDisplay.each(function(node) {
|
||||
node.destroy();
|
||||
});
|
||||
|
||||
// Input string display title
|
||||
makeText(520, 50, "Input: string s = \"" + str + "\"", "input_title");
|
||||
|
||||
// Create boxes for each character
|
||||
var boxWidth = 40;
|
||||
var boxSpacing = 10;
|
||||
var startX = 520;
|
||||
var startY = 100;
|
||||
|
||||
var group = new Konva.Group({
|
||||
id: 'input_display'
|
||||
});
|
||||
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
var charBox = new Konva.Rect({
|
||||
x: startX + i * (boxWidth + boxSpacing),
|
||||
y: startY,
|
||||
width: boxWidth,
|
||||
height: boxWidth,
|
||||
fill: '#f0f0f0',
|
||||
stroke: '#555',
|
||||
strokeWidth: 1,
|
||||
cornerRadius: 4,
|
||||
id: 'char_box_' + i
|
||||
});
|
||||
|
||||
var charText = new Konva.Text({
|
||||
x: startX + i * (boxWidth + boxSpacing) + 12,
|
||||
y: startY + 10,
|
||||
text: str[i],
|
||||
fontSize: 20,
|
||||
fontFamily: 'monospace',
|
||||
fill: 'black',
|
||||
id: 'char_text_' + i
|
||||
});
|
||||
|
||||
group.add(charBox);
|
||||
group.add(charText);
|
||||
}
|
||||
|
||||
// Create the character pointer (initially at first character)
|
||||
var pointer = new Konva.RegularPolygon({
|
||||
x: startX + boxWidth/2,
|
||||
y: startY - 15,
|
||||
sides: 3,
|
||||
radius: 12,
|
||||
fill: 'red',
|
||||
rotation: 180,
|
||||
id: 'char_pointer'
|
||||
});
|
||||
|
||||
group.add(pointer);
|
||||
layer.add(group);
|
||||
}
|
||||
|
||||
// Create the result and explanations
|
||||
function createResultDisplay() {
|
||||
var prevDisplay = stage.find('#result_display');
|
||||
prevDisplay.each(function(node) {
|
||||
node.destroy();
|
||||
});
|
||||
|
||||
var group = new Konva.Group({
|
||||
id: 'result_display'
|
||||
});
|
||||
|
||||
// Result display
|
||||
var resultBox = new Konva.Rect({
|
||||
x: 520,
|
||||
y: 150,
|
||||
width: 250,
|
||||
height: 40,
|
||||
fill: '#e0e0e0',
|
||||
stroke: '#555',
|
||||
strokeWidth: 1,
|
||||
cornerRadius: 5,
|
||||
id: 'result_box'
|
||||
});
|
||||
|
||||
var resultText = new Konva.Text({
|
||||
x: 530,
|
||||
y: 160,
|
||||
text: 'Output: Code Not Finished',
|
||||
fontSize: 16,
|
||||
fontFamily: 'Arial',
|
||||
fill: 'black',
|
||||
id: 'result_text'
|
||||
});
|
||||
|
||||
// Explanation box
|
||||
var explanationBox = new Konva.Rect({
|
||||
x: 520,
|
||||
y: 200,
|
||||
width: 250,
|
||||
height: 150,
|
||||
fill: '#f0f0f0',
|
||||
stroke: '#555',
|
||||
strokeWidth: 1,
|
||||
cornerRadius: 5,
|
||||
id: 'explanation_box'
|
||||
});
|
||||
|
||||
var explanationText = new Konva.Text({
|
||||
x: 530,
|
||||
y: 210,
|
||||
text: 'Click "Next Step" to begin checking the brackets.',
|
||||
fontSize: 16,
|
||||
fontFamily: 'Arial',
|
||||
fill: 'black',
|
||||
width: 240,
|
||||
id: 'explanation_text'
|
||||
});
|
||||
|
||||
group.add(resultBox);
|
||||
group.add(resultText);
|
||||
group.add(explanationBox);
|
||||
group.add(explanationText);
|
||||
|
||||
layer.add(group);
|
||||
}
|
||||
|
||||
// Utility function to create text
|
||||
function makeText(x, y, str, id, color) {
|
||||
var text = new Konva.Text({
|
||||
x: x,
|
||||
y: y,
|
||||
text: str,
|
||||
id: 'text_' + id,
|
||||
fontSize: 16,
|
||||
fontFamily: 'Arial',
|
||||
fill: color || 'black',
|
||||
width: 1000,
|
||||
});
|
||||
layer.add(text);
|
||||
return text;
|
||||
}
|
||||
|
||||
// highlight current line in code
|
||||
function highlightCodeLine(lineNum) {
|
||||
for (var i = 0; i < 25; i++) {
|
||||
var line = stage.find('#text_code_line_' + i);
|
||||
if (line.length > 0) {
|
||||
line[0].fontStyle('normal');
|
||||
line[0].fill('black');
|
||||
}
|
||||
}
|
||||
|
||||
// highlight current line
|
||||
var currentLine = stage.find('#text_code_line_' + lineNum);
|
||||
if (currentLine.length > 0) {
|
||||
currentLine[0].fontStyle('bold');
|
||||
currentLine[0].fill('blue');
|
||||
}
|
||||
|
||||
layer.draw();
|
||||
}
|
||||
|
||||
// Move the character pointer
|
||||
function movePointer(index) {
|
||||
if (index < 0 || index >= currentString.length) return;
|
||||
|
||||
var charBox = stage.find('#char_box_' + index)[0];
|
||||
var pointer = stage.find('#char_pointer')[0];
|
||||
|
||||
if (charBox && pointer) {
|
||||
pointer.x(charBox.x() + charBox.width()/2);
|
||||
}
|
||||
|
||||
layer.draw();
|
||||
}
|
||||
|
||||
|
||||
// Reset character highlight
|
||||
function resetCharHighlight(index) {
|
||||
if (index < 0 || index >= currentString.length) return;
|
||||
|
||||
var charBox = stage.find('#char_box_' + index)[0];
|
||||
if (charBox) {
|
||||
charBox.fill('#f0f0f0');
|
||||
}
|
||||
|
||||
layer.draw();
|
||||
}
|
||||
|
||||
// Push a character onto the stack
|
||||
function pushToStack(char) {
|
||||
stack.push(char);
|
||||
var stackIndex = stack_size - stack.length;
|
||||
|
||||
// Add the character to the stack visual
|
||||
var charText = new Konva.Text({
|
||||
x: stack_x + stack_width/2 - 10,
|
||||
y: stack_y + stackIndex * stack_height + 10,
|
||||
text: char,
|
||||
fontSize: 24,
|
||||
fontFamily: 'monospace',
|
||||
fill: 'black',
|
||||
id: 'stack_item_' + stackIndex
|
||||
});
|
||||
|
||||
layer.add(charText);
|
||||
layer.draw();
|
||||
}
|
||||
|
||||
// Pop a character from the stack
|
||||
function popFromStack() {
|
||||
if (stack.length === 0) return null;
|
||||
|
||||
var poppedChar = stack.pop();
|
||||
var stackIndex = stack_size - stack.length - 1;
|
||||
|
||||
// Remove the character from the stack visual
|
||||
stage.find('#stack_item_' + stackIndex).destroy();
|
||||
|
||||
// Reset the stack slot color
|
||||
stage.find('#stack_rec' + stackIndex)[0].fill('#f0f0f0');
|
||||
|
||||
layer.draw();
|
||||
return poppedChar;
|
||||
}
|
||||
|
||||
|
||||
// Update the result text
|
||||
function updateResult(text, color) {
|
||||
var resultText = stage.find('#result_text')[0];
|
||||
if (resultText) {
|
||||
resultText.text('Output: ' + text);
|
||||
resultText.fill(color || 'black');
|
||||
}
|
||||
layer.draw();
|
||||
}
|
||||
|
||||
// Update the explanation text
|
||||
function updateExplanation(text) {
|
||||
var explanationText = stage.find('#explanation_text')[0];
|
||||
if (explanationText) {
|
||||
explanationText.text(text);
|
||||
}
|
||||
layer.draw();
|
||||
}
|
||||
|
||||
// Reset the animation state for a new case
|
||||
function resetAnimation(str) {
|
||||
// Reset state variables
|
||||
currentString = str;
|
||||
currentIndex = 0;
|
||||
stack = [];
|
||||
pc = 1;
|
||||
isComplete = false;
|
||||
|
||||
// Reset visual elements
|
||||
init();
|
||||
createInputDisplay(str);
|
||||
createResultDisplay();
|
||||
|
||||
// Reset all stack slots
|
||||
for (var i = 0; i < stack_size; i++) {
|
||||
stage.find('#stack_rec' + i)[0].fill('#f0f0f0');
|
||||
}
|
||||
|
||||
// Initialize first step
|
||||
highlightCodeLine(0);
|
||||
updateResult('Code Not Finished', 'black');
|
||||
updateExplanation('Click "Next Step" to begin checking the brackets: ' + str);
|
||||
|
||||
layer.draw();
|
||||
}
|
||||
|
||||
// Process the next step of the animation
|
||||
function nextStep() {
|
||||
if (isComplete) return;
|
||||
|
||||
switch (pc) {
|
||||
case 1:
|
||||
// Initialize stack
|
||||
highlightCodeLine(1);
|
||||
updateExplanation("Creating an empty stack to store opening brackets");
|
||||
pc++;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Get length
|
||||
highlightCodeLine(2);
|
||||
updateExplanation("Getting the length of the string of brackets: " + currentString.length);
|
||||
pc++;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// Initialize for loop
|
||||
highlightCodeLine(4);
|
||||
updateExplanation("Starting the loop to process each character");
|
||||
pc++;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// Check if done with all characters
|
||||
if (currentIndex >= currentString.length) {
|
||||
// Final check if stack is empty
|
||||
highlightCodeLine(21);
|
||||
updateExplanation("All characters processed. Checking if the stack is empty...");
|
||||
|
||||
if (stack.length === 0) {
|
||||
updateResult("True");
|
||||
updateExplanation("The stack is empty, which means all opening brackets were matched with their closing brackets in the correct order. The string is valid.");
|
||||
} else {
|
||||
updateResult("False", "red");
|
||||
updateExplanation("The stack still has " + stack.length + " unmatched opening bracket(s). This means some opening brackets were never matched with closing brackets. The string is invalid.");
|
||||
}
|
||||
|
||||
isComplete = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Process current character
|
||||
movePointer(currentIndex);
|
||||
var currentChar = currentString[currentIndex];
|
||||
updateExplanation("Processing character at index " + currentIndex + ": '" + currentChar + "'");
|
||||
|
||||
// Check if opening bracket
|
||||
if (currentChar === '(' || currentChar === '{' || currentChar === '[') {
|
||||
highlightCodeLine(6);
|
||||
pc = 5; // Go to opening bracket logic
|
||||
} else {
|
||||
highlightCodeLine(8);
|
||||
pc = 6; // Go to closing bracket logic
|
||||
}
|
||||
break;
|
||||
|
||||
case 5:
|
||||
// Process opening bracket
|
||||
var currentChar = currentString[currentIndex];
|
||||
highlightCodeLine(7);
|
||||
updateExplanation("Found opening bracket '" + currentChar + "'. Pushing it onto the stack to remember it.");
|
||||
pushToStack(currentChar);
|
||||
|
||||
// Move to next character
|
||||
resetCharHighlight(currentIndex);
|
||||
currentIndex++;
|
||||
pc = 4; // Return to character processing
|
||||
break;
|
||||
|
||||
case 6:
|
||||
// Process closing bracket - check if stack is empty
|
||||
var currentChar = currentString[currentIndex];
|
||||
highlightCodeLine(10);
|
||||
|
||||
if (stack.length === 0) {
|
||||
updateExplanation("Found closing bracket '" + currentChar + "' but the stack is empty. This means there's no matching opening bracket, so the string is invalid.");
|
||||
updateResult("false", "red");
|
||||
isComplete = true;
|
||||
break;
|
||||
}
|
||||
|
||||
updateExplanation("Found closing bracket '" + currentChar + "'. Checking the top of the stack for a matching opening bracket.");
|
||||
pc++;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
// Get top of stack
|
||||
var currentChar = currentString[currentIndex];
|
||||
var topChar = stack[stack.length - 1];
|
||||
highlightCodeLine(12);
|
||||
updateExplanation("The top of the stack contains: '" + topChar + "'");
|
||||
pc++;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
// Pop from stack
|
||||
var currentChar = currentString[currentIndex];
|
||||
var topChar = stack[stack.length - 1];
|
||||
highlightCodeLine(13);
|
||||
updateExplanation("Popping '" + topChar + "' from the stack to compare with '" + currentChar + "'");
|
||||
lastPopped = popFromStack();
|
||||
pc++;
|
||||
break;
|
||||
|
||||
case 9:
|
||||
// Check if brackets match
|
||||
var currentChar = currentString[currentIndex];
|
||||
var topChar = lastPopped;
|
||||
if (currentChar === ')') {
|
||||
highlightCodeLine(15);
|
||||
} else if (currentChar === '}') {
|
||||
highlightCodeLine(16);
|
||||
} else if (currentChar === ']') {
|
||||
highlightCodeLine(17);
|
||||
}
|
||||
|
||||
// Check if match failed
|
||||
if ((currentChar === ')' && topChar !== '(') ||
|
||||
(currentChar === '}' && topChar !== '{') ||
|
||||
(currentChar === ']' && topChar !== '[')) {
|
||||
|
||||
updateExplanation("Mismatch! Closing bracket '" + currentChar + "' doesn't match with the opening bracket '" + topChar + "'. The string is invalid.");
|
||||
updateResult("False");
|
||||
isComplete = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Match succeeded
|
||||
updateExplanation("Match found! Closing bracket '" + currentChar + "' correctly matches with opening bracket '" + topChar + "'." );
|
||||
|
||||
// Move to next character
|
||||
resetCharHighlight(currentIndex);
|
||||
currentIndex++;
|
||||
pc = 4; // Return to character processing
|
||||
break;
|
||||
}
|
||||
|
||||
layer.draw();
|
||||
}
|
||||
|
||||
// Case 1: "()"
|
||||
function case1() {
|
||||
resetAnimation("()");
|
||||
}
|
||||
|
||||
// Case 2: "()[]{}"
|
||||
function case2() {
|
||||
resetAnimation("()[]{}");
|
||||
}
|
||||
|
||||
// Case 3: "(]"
|
||||
function case3() {
|
||||
resetAnimation("(]");
|
||||
}
|
||||
|
||||
// Case 4: "([])"
|
||||
function case4() {
|
||||
resetAnimation("([])");
|
||||
}
|
||||
|
||||
// Case 5: "({[{[()]}]})"
|
||||
function case5() {
|
||||
resetAnimation("{[([])]}");
|
||||
}
|
||||
|
||||
// Initialize the animation
|
||||
init();
|
||||
case1(); // Start with Case 1 by default
|
||||
@@ -1,11 +1,10 @@
|
||||
**Special Note 1: A correct program does not necessarily pass all the test cases for this assignment, as Submitty may let you fail a test case if your program is not fast enough or consumes too much memory.**
|
||||
**Special Note 2: For this assignment, we will not deduct points if you use data structures which have not been learned in this class. However, students who passed all test cases last semester did not use any of such data structures. In other words, using only data structures we have learned so far, is sufficient to pass all test cases.**
|
||||
|
||||
# Homework 6 — Inverse Word Search Recursion
|
||||
|
||||
In this homework we will build an inverse word search program using the techniques of recursion.
|
||||
The goal is to construct a grid of letters that one can search to find specific words. Understanding the non-linear word
|
||||
search program from Lectures 12 will be helpful in thinking about how you will solve this problem.
|
||||
search program from Lectures 12/13 will be helpful in thinking about how you will solve this problem.
|
||||
We strongly urge you to study and play with that program, including tracing through its behavior using a
|
||||
debugger or cout statements or both. Please read the entire handout before beginning your implementation.
|
||||
|
||||
@@ -50,38 +49,56 @@ solution, your program should just output the first legal solution it finds (it
|
||||
number of solutions, nor does it need to be the first solution shown in our output). If the puzzle is impossible
|
||||
your program should output “No solutions found”.
|
||||
|
||||
**To implement this assignment, you must use recursion in your search.** First you should tackle the problem
|
||||
of finding and outputting one legal solution to the puzzle (if one exists).
|
||||
**To implement this assignment, you must use recursion in your search.** First you should tackle the problem of finding and outputting one legal solution to the puzzle (if one exists).
|
||||
|
||||
## Algorithm Analysis
|
||||
<!-- ## Algorithm Analysis
|
||||
|
||||
For larger, more complex examples, this is a really hard problem. Your program should be able to handle
|
||||
the small puzzles we have created in a reasonable amount of time. <!--You should make up your own test cases
|
||||
as well to understand this complexity. Include these test cases with your submission (they will be graded).
|
||||
Summarize the results of your testing, which test cases completed successfully and the approximate “wall
|
||||
clock time” for completion of each test.--> The UNIX/WSL time command can be prepended to your command
|
||||
line to estimate the running time:
|
||||
For larger, more complex examples, this is a really hard problem. Your program should be able to handle the small puzzles we have created in a reasonable amount of time. You should make up your own test cases as well to understand this complexity. Include these test cases with your submission (they will be graded).
|
||||
Summarize the results of your testing, which test cases completed successfully and the approximate “wall clock time” for completion of each test. The UNIX/WSL time command can be prepended to your command line to estimate the running time:
|
||||
|
||||
```console
|
||||
time inverse_word_search.exe puzzle1.txt out1.txt one_solution
|
||||
```
|
||||
|
||||
Once you have finished your implementation and testing, analyze the performance of your algorithm using
|
||||
order notation. What important variables control the complexity of a particular problem? The width &
|
||||
height of the grid (w and h), the number of required words (r), the number of forbidden words (f), the
|
||||
number of letters in each word (l), the number of solutions (s)? In your plain text README.txt file, write
|
||||
a concise paragraph (< 200 words) justifying your answer. <!--Also include a simple table summarizing the
|
||||
running time and number of solutions found by your program on each of the provided examples. Note: It’s
|
||||
ok if your program can’t solve the biggest puzzles in a reasonable amount of time.-->
|
||||
Once you have finished your implementation and testing, analyze the performance of your algorithm using order notation. What important variables control the complexity of a particular problem? The width & height of the grid (w and h), the number of required words (r), the number of forbidden words (f), the number of letters in each word (l), the number of solutions (s)? In your plain text README.txt file, write a concise paragraph (< 200 words) justifying your answer. Also include a simple table summarizing the running time and number of solutions found by your program on each of the provided examples. Note: It’s ok if your program can’t solve the biggest puzzles in a reasonable amount of time.-->
|
||||
|
||||
## Program Requirements & Submission Details
|
||||
|
||||
Use good coding style when you design and implement your program. Organize your program into functions:
|
||||
don’t put all the code in main! Be sure to read the [Homework Policies](https://www.cs.rpi.edu/academics/courses/spring24/csci1200/homework_policies.php) as you put the finishing touches on your solution. Be sure to make up new test cases to fully debug your program and don’t forget
|
||||
don’t put all the code in main! Be sure to read the [Homework Policies](https://www.cs.rpi.edu/academics/courses/spring25/csci1200/homework_policies.php) as you put the finishing touches on your solution. Be sure to make up new test cases to fully debug your program and don’t forget
|
||||
to comment your code! Use the provided template [README.txt](./README.txt) file for notes you want the grader to read.
|
||||
You must do this assignment on your own, as described in the [Collaboration Policy & Academic Integrity](https://www.cs.rpi.edu/academics/courses/spring24/csci1200/academic_integrity.php) page. If you did discuss the problem or error messages, etc. with anyone, please list their names in your README.txt file.
|
||||
You must do this assignment on your own, as described in the [Collaboration Policy & Academic Integrity](https://www.cs.rpi.edu/academics/courses/spring25/csci1200/academic_integrity.php) page. If you did discuss the problem or error messages, etc. with anyone, please list their names in your README.txt file.
|
||||
|
||||
**Due Date**: 03/14/2024, Thursday, 10pm.
|
||||
**Due Date**: 03/13/2025, Thursday, 10pm.
|
||||
|
||||
## FAQs
|
||||
|
||||
1. Q: Am I allowed to use data structures not covered in this class?
|
||||
A: Yes. For this homework, you can use whatever data structures you want to use; but last year, the student who topped the leaderboard with a very large margin, did not use any fancy data structures. The student used std::set, but did not use std::unordered_set or std::unordered_map, or any data structures which had not been covered in this class.
|
||||
|
||||
2. Q: Can I use global variables for this homework?
|
||||
A: Yes, you can.
|
||||
|
||||
3. Q: Do I have to implement a class or classes?
|
||||
A: It's not a requirement. You can if you want to.
|
||||
|
||||
4. Q: Which optimization flag does the Submitty compiler use?
|
||||
A: Submitty uses -O3 for this homework.
|
||||
|
||||
5. Q: How many submissions can I make?
|
||||
A: 40. Submitty will deduct points once you submit more than 40 times.
|
||||
|
||||
6. Q: If I fill in all the words in the grid and there are still some empty slots, what characters do I fill in the these extra spaces?
|
||||
A: You can fill in any lower case letter there and make sure it doesn't introduce a forbidden word. A single letter gives you one solution; replacing it with another letter would give you another solution, and in total, this would lead to 26 potential solutions, since there are 26 lower case letters.
|
||||
|
||||
7. Q: What bonus do I get if my program ranks higher than the instructor's program on the leaderboard?
|
||||
A: If by Thursday night 10pm (which is the submission deadline), your program ranks higher than the instructor's program, you have many options. You can choose one of these:
|
||||
- Drop the lowest test score
|
||||
- Drop the lowest homework
|
||||
- Skip 1 future homework
|
||||
- Skip test 3
|
||||
|
||||
You will receive an email asking you about which option you want to choose, or if you want to propose a different option.
|
||||
|
||||
## Rubric
|
||||
|
||||
@@ -90,15 +107,16 @@ You must do this assignment on your own, as described in the [Collaboration Poli
|
||||
- One of name, collaborators, or hours not filled in. (-1)
|
||||
- Two or more of name, collaborators, or hours not filled in. (-2)
|
||||
- No reflection. (-1)
|
||||
- LETTER GRID REPRESENTATION (2 pts)
|
||||
- Grid is not represented via nested structure vector<vector<char>>, vector<vector<string>>, vector<string>, char\*\*, etc. (-1)
|
||||
<!-- - LETTER GRID REPRESENTATION (3 pts)
|
||||
- Grid is not represented via nested structure vector<vector<char>>, vector<vector<string>>, vector<string>, char\*\*, etc. (-2)
|
||||
- Lookup of a position is not O(1), uses something like<list<char>> which has lookup of O(n). (-1)
|
||||
- Incomplete to the point that no grid representation is evident within their code. (-1)
|
||||
- USES RECURSION (4 pts)
|
||||
- Use some non-trivial recursion but doesn’t use recursion in the search process of board creation. (-2)
|
||||
- Uses recursion but only trivially. (-3)
|
||||
- Does not use any recursion. (-4)
|
||||
- ALGORITHM ANALYSIS (In terms of the grid dimensions, the # of words, # of letters per word, the number of solutions etc. Looking for both an answer in order notation and a well-written justification in the plaintext README.txt file.) (5 pts)
|
||||
- Incomplete to the point that no grid representation is evident within their code. (-2)
|
||||
-->
|
||||
- USES RECURSION (10 pts)
|
||||
- Use some non-trivial recursion but doesn’t use recursion in the search process of board creation. (-3)
|
||||
- Uses recursion but only trivially. (-7)
|
||||
- Does not use any recursion. (-10)
|
||||
<!-- - ALGORITHM ANALYSIS (In terms of the grid dimensions, the # of words, # of letters per word, the number of solutions etc. Looking for both an answer in order notation and a well-written justification in the plaintext README.txt file.) (5 pts)
|
||||
- No order notation provided (-5)
|
||||
- Order notation not written in terms of the provided variables w,h,r,f,l,s. Introduces new vars or provides it just in terms of n. (-2)
|
||||
- Incorrect order notation. (-2)
|
||||
@@ -108,19 +126,16 @@ You must do this assignment on your own, as described in the [Collaboration Poli
|
||||
- Did not finish but provides a reasonable analysis with respect to a theoretical implementation and properly justifies it. (-2)
|
||||
- Did not finish but provides a runtime and some small analysis for a theoretical solution. (-4)
|
||||
- Correct order notation for a largely incomplete implementation. (-4)
|
||||
<!-- - TESTING SUMMARY & NEW TEST CASES (Included with submission and discussed in README.txt) (3 pts)
|
||||
- TESTING SUMMARY & NEW TEST CASES (Included with submission and discussed in README.txt) (3 pts)
|
||||
- Does not provide an adequate description of what the new testcases were in the README. (-2)
|
||||
- Did not provide running times of the new test cases. (-1)
|
||||
- Provides new test case description but implementation/test was missing from the submission. (-1)
|
||||
- Did not provide new test cases or implementation too incomplete for new test cases. (-3)-->
|
||||
- PROGRAM STRUCTURE (6 pts)
|
||||
- PROGRAM STRUCTURE (7 pts)
|
||||
- Putting almost everything in the main function. It's better to create separate functions for different tasks. (-2)
|
||||
- Function bodies containing more than one statement are placed in the .h file. (okay for templated classes) (-2)
|
||||
- Missing include guards in the .h file. (Or does not declare them correctly) (-1)
|
||||
- Functions are not well documented or are poorly commented, in either the .h or the .cpp file. (-1)
|
||||
- Improper uses or omissions of const and reference. (-1)
|
||||
- At least one function is excessively long (i.e., more than 200 lines). (-1)
|
||||
- Overly cramped, excessive whitespace, or poor indentation. (-1)
|
||||
- Poor file organization: Puts more than one class in a file (okay for very small helper classes) (-1)
|
||||
- Poor variable names. (-1)
|
||||
- Contains useless comments like commented-out code, terminal commands, or silly notes. (-1)
|
||||
- Poor choice of variable names: non-descriptive names (e.g. 'vec', 'str', 'var'), single-letter variable names (except single loop counter), etc. (-2)
|
||||
@@ -18,13 +18,6 @@ own, as described in "Academic Integrity for Homework" handout.
|
||||
ESTIMATE OF # OF HOURS SPENT ON THIS ASSIGNMENT: < insert # hours >
|
||||
|
||||
|
||||
ALGORITHM ANALYSIS:
|
||||
What's the order notation of your algorithm?
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
MISC. COMMENTS TO GRADER:
|
||||
Optional, please be concise!
|
||||
|
||||
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
4
hws/inverse_word_search/test10.txt
Normal file
4
hws/inverse_word_search/test10.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
3 2
|
||||
+ cat
|
||||
+ one
|
||||
- on
|
||||
13
hws/inverse_word_search/test11.txt
Normal file
13
hws/inverse_word_search/test11.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
7 6
|
||||
+ chore
|
||||
+ anchor
|
||||
+ horse
|
||||
+ melee
|
||||
+ wear
|
||||
+ beast
|
||||
+ oboe
|
||||
+ bow
|
||||
+ bottle
|
||||
+ beaches
|
||||
+ laser
|
||||
+ wobbler
|
||||
22
hws/inverse_word_search/test12.txt
Normal file
22
hws/inverse_word_search/test12.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
15 3
|
||||
+ aardvark
|
||||
+ lilith
|
||||
+ amount
|
||||
+ tilde
|
||||
+ true
|
||||
+ bore
|
||||
+ loud
|
||||
+ illicit
|
||||
+ lll
|
||||
+ aaa
|
||||
+ ama
|
||||
+ aba
|
||||
- hat
|
||||
- bh
|
||||
- el
|
||||
- al
|
||||
- ul
|
||||
- at
|
||||
- vo
|
||||
- ve
|
||||
- vb
|
||||
12
hws/inverse_word_search/test13.txt
Normal file
12
hws/inverse_word_search/test13.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
7 5
|
||||
+ beans
|
||||
+ erasing
|
||||
+ amaze
|
||||
+ arm
|
||||
+ soapy
|
||||
+ moo
|
||||
+ normal
|
||||
+ sling
|
||||
+ bakes
|
||||
+ go
|
||||
+ gang
|
||||
17
hws/inverse_word_search/test14.txt
Normal file
17
hws/inverse_word_search/test14.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
5 9
|
||||
+ wacky
|
||||
+ wednesday
|
||||
+ deans
|
||||
+ sear
|
||||
+ youre
|
||||
+ ode
|
||||
+ random
|
||||
+ mosh
|
||||
+ shado
|
||||
+ mean
|
||||
+ moose
|
||||
+ sombody
|
||||
+ kalm
|
||||
+ sake
|
||||
+ old
|
||||
+ code
|
||||
17
hws/inverse_word_search/test15.txt
Normal file
17
hws/inverse_word_search/test15.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
10 5
|
||||
+ fiery
|
||||
+ human
|
||||
+ alien
|
||||
+ incredible
|
||||
+ ship
|
||||
+ pea
|
||||
+ patron
|
||||
+ nepal
|
||||
+ retry
|
||||
+ rap
|
||||
+ aesh
|
||||
+ shapes
|
||||
+ cram
|
||||
+ herse
|
||||
+ minas
|
||||
+ semen
|
||||
25
hws/inverse_word_search/test9.txt
Normal file
25
hws/inverse_word_search/test9.txt
Normal file
@@ -0,0 +1,25 @@
|
||||
4 2
|
||||
+ pin
|
||||
+ pine
|
||||
+ nip
|
||||
+ ink
|
||||
- a
|
||||
- b
|
||||
- c
|
||||
- d
|
||||
- f
|
||||
- g
|
||||
- h
|
||||
- j
|
||||
- l
|
||||
- m
|
||||
- o
|
||||
- q
|
||||
- r
|
||||
- s
|
||||
- t
|
||||
- x
|
||||
- v
|
||||
- w
|
||||
- y
|
||||
- z
|
||||
@@ -1,4 +1,4 @@
|
||||
# Lab 9 — Maps
|
||||
# Lab 7 — Maps
|
||||
|
||||
This lab gives you practice initial practice in working with the STL associative container, maps. No downloads
|
||||
are needed until Checkpoint 3.
|
||||
|
||||
@@ -259,9 +259,71 @@ int main() {
|
||||
|
||||
You can compile and run this above [program](queue.cpp).
|
||||
|
||||
## 14.3 Leetcode Exercises
|
||||
## 14.3 Stack Example Problem
|
||||
|
||||
Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.
|
||||
|
||||
An input string is valid if:
|
||||
|
||||
- Open brackets must be closed by the same type of brackets.
|
||||
- Open brackets must be closed in the correct order.
|
||||
- Every close bracket has a corresponding open bracket of the same type.
|
||||
|
||||
Example 1: Input: s = "()" Output: true
|
||||
|
||||
Example 2: Input: s = "()[]{}" Output: true
|
||||
|
||||
Example 3: Input: s = "(]" Output: false
|
||||
|
||||
Example 4: Input: s = "([])" Output: true
|
||||
|
||||
Constraints: 1 <= s.length <= 104; s consists of parentheses only '()[]{}'.
|
||||
|
||||
We have the solution here:
|
||||
|
||||
```cpp
|
||||
bool isValid(string s) {
|
||||
std::stack<char> myStack;
|
||||
int len = s.length();
|
||||
char c;
|
||||
for(int i=0;i<len;i++){
|
||||
// push all the open brackets into the stack
|
||||
if(s[i]=='(' || s[i]=='{' || s[i]=='['){
|
||||
myStack.push(s[i]);
|
||||
}else{
|
||||
// if we encounter a close bracket first, it's already invalid.
|
||||
if(myStack.empty()){
|
||||
return false;
|
||||
}
|
||||
c = myStack.top();
|
||||
myStack.pop();
|
||||
// for every close bracket we encounter, there must be a corresponding open bracket at the top of the stack.
|
||||
if(s[i]==')' && c!='('){
|
||||
return false;
|
||||
}
|
||||
if(s[i]=='}' && c!='{'){
|
||||
return false;
|
||||
}
|
||||
if(s[i]==']' && c!='['){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if it's empty, we are good.
|
||||
if(myStack.empty()){
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- - [Leetcode problem 20: Valid Parentheses](https://leetcode.com/problems/valid-parentheses/). Solution: [p20_valid_parentheses.cpp](../../leetcode/p20_valid_parentheses.cpp) -->
|
||||
|
||||
- Play this [animation](https://jidongxiao.github.io/CSCI1200-DataStructures/animations/stack/index.html) to see how this program works.
|
||||
|
||||
## 14.4 More Leetcode Exercises
|
||||
|
||||
- [Leetcode problem 225: Implement Stack using Queues](https://leetcode.com/problems/implement-stack-using-queues/). Solution: [p225_stack_using_queues.cpp](../../leetcode/p225_stack_using_queues.cpp).
|
||||
- [Leetcode problem 232: Implement Queue using Stacks](https://leetcode.com/problems/implement-queue-using-stacks/). Solution: [p232_queue_using_stacks.cpp](../../leetcode/p232_queue_using_stacks.cpp).
|
||||
- [Leetcode problem 20: Valid Parentheses](https://leetcode.com/problems/valid-parentheses/). Solution: [p20_valid_parentheses.cpp](../../leetcode/p20_valid_parentheses.cpp)
|
||||
- [Leetcode problem 71: Simplify Path](https://leetcode.com/problems/simplify-path/). Solution: [p71_simplify_path.cpp](../../leetcode/p71_simplify_path.cpp)
|
||||
|
||||
@@ -1,41 +1,81 @@
|
||||
# Lecture 15 --- Associative Containers (Maps), Part 1
|
||||
|
||||
Associative Containers (STL Maps)
|
||||
- STL Pairs
|
||||
- STL Maps: associative containers for fast insert, access and remove
|
||||
- Example: Counting word occurrences
|
||||
- STL Pairs
|
||||
- Map iterators
|
||||
- Map member functions: operator[], find, insert, erase.
|
||||
- Efficiency
|
||||
- STL maps vs. STL vectors vs. STL lists
|
||||
|
||||
## 15.1 STL Maps: Associative Containers
|
||||
## 15.1 STL Pairs
|
||||
|
||||
- std::pair is a templated struct with just two members, called first and second.
|
||||
|
||||
```cpp
|
||||
template <typename T1, typename T2>
|
||||
struct pair {
|
||||
T1 first;
|
||||
T2 second;
|
||||
|
||||
// Constructors
|
||||
constexpr pair();
|
||||
constexpr pair(const T1& x, const T2& y);
|
||||
|
||||
// Other member functions...
|
||||
};
|
||||
```
|
||||
|
||||
**struct** in C++ is identical to class except that members are public by default, std::pair is implemented as a struct to allow direct access to its first and second members without requiring explicit getter functions.
|
||||
- To work with pairs, you must #include <utility>. Note that the header file for maps (#include <map>) itself includes utility, so you don't have to include utility explicitly when you use pairs with maps.
|
||||
- Here are simple examples of manipulating pairs:
|
||||
```cpp
|
||||
std::pair<int, double> p1(5, 7.5);
|
||||
std::pair<int, double> p2 = std::make_pair(8, 9.5);
|
||||
p1.first = p2.first;
|
||||
p2.second = 13.3;
|
||||
std::cout << p1.first << " " << p1.second << std::endl;
|
||||
std::cout << p2.first << " " << p2.second << std::endl;
|
||||
p1 = p2;
|
||||
std::pair<const std::string, double> p3 = std::make_pair(std::string("hello"), 3.5);
|
||||
p3.second = -1.5;
|
||||
// p3.first = std::string("illegal"); // (a)
|
||||
// p1 = p3; // (b)
|
||||
```
|
||||
- The function std::make_pair creates a pair object from the given values. It is really just a simplified
|
||||
constructor, and as the example shows there are other ways of constructing pairs.
|
||||
- Most of the statements in the above code show accessing and changing values in pairs.
|
||||
The two statements at the end are commented out because they cause syntax errors:
|
||||
- In (a), the first entry of p3 is const, which means it can’t be changed.
|
||||
- In (b), the two pairs are different types! Make sure you understand this.
|
||||
|
||||
## 15.2 STL Maps: Associative Containers
|
||||
|
||||
- STL maps store pairs of “associated” values.
|
||||
We will see several examples today, in Lab 9, and in Lecture 16:
|
||||
- An association between a string, representing a word, and an int representing the number of times that
|
||||
- e.g., An association between a string, representing a word, and an int representing the number of times that
|
||||
word has been seen in an input file.
|
||||
- An association between a string, representing a word, and a vector that stores the line numbers from a
|
||||
text file on which that string occurs (next lecture).
|
||||
- An association between a phone number and the name of the person with that number (Lab 9).
|
||||
- An association between a class object representing a student name and the student’s info (next lecture).
|
||||
- e.g., An association between a string, representing a word, and a vector that stores the line numbers from a
|
||||
text file on which that string occurs.
|
||||
- e.g., An association between a phone number and the name of the person with that number.
|
||||
- e.g., An association between a class object representing a student name and the student’s info.
|
||||
A particular instance of a map is defined (declared) with the syntax:
|
||||
std::map<key_type, value_type> var_name
|
||||
In our first two examples above, key type is a string. In the first example, the value type is an int and in
|
||||
|
||||
```cpp
|
||||
std::map<key_type, value_type> var_name
|
||||
```
|
||||
- In our first two examples above, key type is a string. In the first example, the value type is an int and in
|
||||
the second it is a std::vector<int>.
|
||||
- Entries in maps are pairs: std::pair<const key_type, value_type>, or just std::pair<key_type, value_type>.
|
||||
- Entries in maps are pairs: std::pair<const key_type, value_type>, the const is needed to ensure that the keys aren’t changed! This is crucial because maps are sorted by keys!
|
||||
- Map iterators refer to pairs.
|
||||
- Map search, insert and erase are all very fast: O(log n) time, where n is the number of pairs stored in the map.
|
||||
Note: The STL map type has similarities to the Python dictionary, Java HashMap, or a Perl hash, but the
|
||||
<!-- Note: The STL map type has similarities to the Python dictionary, Java HashMap, or a Perl hash, but the
|
||||
data structures are not the same. The organization, implementation, and performance is different. In a couple
|
||||
weeks we’ll see an STL data structure that is even more similar to the Python dictionary.
|
||||
- Map search, insert and erase are O(log n).
|
||||
First, let’s see how some of this works with a program to count the occurrences of each word in a file. We’ll look
|
||||
at more details and more examples later.
|
||||
weeks we’ll see an STL data structure that is even more similar to the Python dictionary.-->
|
||||
|
||||
## 15.2 Counting Word Occurrences
|
||||
## 15.3 Counting Word Occurrences
|
||||
|
||||
Here’s a simple and elegant solution to this problem using a map:
|
||||
First, let’s see how some of this works with a program to count the occurrences of each word in a file. Here’s a simple and elegant solution to this problem using a map:
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
@@ -65,37 +105,6 @@ must be an operator< defined for the key.
|
||||
changed. It can only be erased (together with the associated value).
|
||||
- Duplicate keys can not be in the map.
|
||||
|
||||
## 15.4 STL Pairs
|
||||
|
||||
The mechanics of using std::pairs are relatively straightforward:
|
||||
- std::pairs are a templated struct with just two members, called first and second. Reminder: a struct
|
||||
is basically a wimpy class.
|
||||
- To work with pairs, you must #include <utility>. Note that the header file for maps (#include <map>)
|
||||
itself includes utility, so you don’t have to include utility explicitly when you use pairs with maps.
|
||||
- Here are simple examples of manipulating pairs:
|
||||
```cpp
|
||||
std::pair<int, double> p1(5, 7.5);
|
||||
std::pair<int, double> p2 = std::make_pair(8, 9.5);
|
||||
p1.first = p2.first;
|
||||
p2.second = 13.3;
|
||||
std::cout << p1.first << " " << p1.second << std::endl;
|
||||
std::cout << p2.first << " " << p2.second << std::endl;
|
||||
p1 = p2;
|
||||
std::pair<const std::string, double> p3 = std::make_pair(std::string("hello"), 3.5);
|
||||
p3.second = -1.5;
|
||||
// p3.first = std::string("illegal"); // (a)
|
||||
// p1 = p3; // (b)
|
||||
```
|
||||
- The function std::make_pair creates a pair object from the given values. It is really just a simplified
|
||||
constructor, and as the example shows there are other ways of constructing pairs.
|
||||
- Most of the statements in the above code show accessing and changing values in pairs.
|
||||
The two statements at the end are commented out because they cause syntax errors:
|
||||
- In (a), the first entry of p3 is const, which means it can’t be changed.
|
||||
- In (b), the two pairs are different types! Make sure you understand this.
|
||||
- Returning to maps, each entry in the map is a pair object of type:
|
||||
std::pair<const key_type, value_type>
|
||||
- The const is needed to ensure that the keys aren’t changed! This is crucial because maps are sorted by keys!
|
||||
|
||||
## 15.5 Maps: operator[]
|
||||
|
||||
- We’ve used the [] operator on vectors, which is conceptually very simple because vectors are just resizable
|
||||
@@ -161,9 +170,71 @@ up to, but not including, last.
|
||||
- size_type erase(const key_type& k) — erase the pair containing key k, returning either 0 or 1, depending
|
||||
on whether or not the key was in a pair in the map.
|
||||
|
||||
## 15.10 Leetcode Exercises
|
||||
## 15.10 Leetcode Exercise
|
||||
|
||||
Given an integer array nums and an integer k, return true if there are two distinct indices i and j in the array such that nums[i] == nums[j] and abs(i - j) <= k.
|
||||
|
||||
We have the solution below.
|
||||
|
||||
```cpp
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
bool containsNearbyDuplicate(std::vector<int>& nums, int k) {
|
||||
int size = nums.size();
|
||||
// create the map, map key is the value of the vector element, map value is the index of that element in the vector.
|
||||
std::map<int,int> map1;
|
||||
for(int i=0;i<size;i++){
|
||||
// if already exists
|
||||
if(map1.find(nums[i])!=map1.end()){
|
||||
if(i-map1[nums[i]]<=k){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
map1[nums[i]]=i;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::vector<std::vector<int>> testCases = {
|
||||
{1, 2, 3, 1}, // Expected: true (nums[0] == nums[3], abs(0-3) <= k)
|
||||
{1, 0, 1, 1}, // Expected: true (nums[2] == nums[3], abs(2-3) <= k)
|
||||
{1, 2, 3, 4, 5}, // Expected: false (no duplicates)
|
||||
{1, 2, 3, 4, 1}, // Expected: true if k >= 4
|
||||
{99, 99}, // Expected: true if k >= 1
|
||||
{1, 2, 3, 4, 5, 6}, // Expected: false (no duplicates)
|
||||
};
|
||||
|
||||
std::vector<int> kValues = {3, 1, 2, 4, 1, 2}; // Corresponding k values for test cases
|
||||
|
||||
for (size_t i = 0; i < testCases.size(); i++) {
|
||||
std::cout << "Test Case " << i + 1 << ": ";
|
||||
bool result = containsNearbyDuplicate(testCases[i], kValues[i]);
|
||||
std::cout << (result ? "true" : "false") << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
When running the above program, we get:
|
||||
```console
|
||||
$ ./a.out
|
||||
Test Case 1: true
|
||||
Test Case 2: true
|
||||
Test Case 3: false
|
||||
Test Case 4: true
|
||||
Test Case 5: true
|
||||
Test Case 6: false
|
||||
```
|
||||
|
||||
You can also find this problem on Leetcode.
|
||||
- [Leetcode problem 219: Contains Duplicate II](https://leetcode.com/problems/contains-duplicate-ii/description/). Solution: [p219_contains_duplicate_ii.cpp](../../leetcode/p219_contains_duplicate_ii.cpp).
|
||||
|
||||
## 15.11 More Leetcode Exercises
|
||||
|
||||
- [Leetcode problem 1: Two Sum](https://leetcode.com/problems/two-sum/). Solution: [p1_twosum.cpp](../../leetcode/p1_twosum.cpp).
|
||||
- [Leetcode problem 219: Contains Duplicate II](https://leetcode.com/problems/contains-duplicate-ii/description/). Solution: [p219_contains_duplicate_ii.cpp](../../leetcode/p219_contains_duplicate_ii.cpp).
|
||||
- [Leetcode problem 290: Word Pattern](https://leetcode.com/problems/word-pattern/). Solution: [p290_word_pattern.cpp](../../leetcode/p290_word_pattern.cpp).
|
||||
|
||||
|
||||
40
lectures/15_maps_I/containsNearbyDuplicate.cpp
Normal file
40
lectures/15_maps_I/containsNearbyDuplicate.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
bool containsNearbyDuplicate(std::vector<int>& nums, int k) {
|
||||
int size = nums.size();
|
||||
// create the map, map key is the value of the vector element, map value is the index of that element in the vector.
|
||||
std::map<int,int> map1;
|
||||
for(int i=0;i<size;i++){
|
||||
// if already exists
|
||||
if(map1.find(nums[i])!=map1.end()){
|
||||
if(i-map1[nums[i]]<=k){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
map1[nums[i]]=i;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::vector<std::vector<int>> testCases = {
|
||||
{1, 2, 3, 1}, // Expected: true (nums[0] == nums[3], abs(0-3) <= k)
|
||||
{1, 0, 1, 1}, // Expected: true (nums[2] == nums[3], abs(2-3) <= k)
|
||||
{1, 2, 3, 4, 5}, // Expected: false (no duplicates)
|
||||
{1, 2, 3, 4, 1}, // Expected: true if k >= 4
|
||||
{99, 99}, // Expected: true if k >= 1
|
||||
{1, 2, 3, 4, 5, 6}, // Expected: false (no duplicates)
|
||||
};
|
||||
|
||||
std::vector<int> kValues = {3, 1, 2, 4, 1, 2}; // Corresponding k values for test cases
|
||||
|
||||
for (size_t i = 0; i < testCases.size(); i++) {
|
||||
std::cout << "Test Case " << i + 1 << ": ";
|
||||
bool result = containsNearbyDuplicate(testCases[i], kValues[i]);
|
||||
std::cout << (result ? "true" : "false") << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,6 +1,20 @@
|
||||
# Lecture 16 --- Associative Containers (Maps), Part 2
|
||||
|
||||
## 16.1 More Complicated Values
|
||||
## 16.1 Map Insert Performance
|
||||
|
||||
One way to improve the map insert performance is using the move constructor. Read [this document](../optimization/map_insert).
|
||||
|
||||
## 16.2 Map Erase Performance
|
||||
|
||||
We learned from previous lecture that we can erase an element from a map via the erase function, and there are two formats:
|
||||
|
||||
- void erase(iterator p) — erase the pair referred to by iterator p.
|
||||
|
||||
- size_type erase(const key_type& k) — erase the pair containing key k, returning either 0 or 1, depending on whether or not the key was in a pair in the map.
|
||||
|
||||
But there is a performance difference between these two. Read [this document](../optimization/map_erase/).
|
||||
|
||||
## 16.3 More Complicated Values
|
||||
|
||||
- Let’s look at the example:
|
||||
```cpp
|
||||
@@ -41,7 +55,7 @@ use of push back or through construction of the vector).
|
||||
- We can figure out the correct syntax for all of these by drawing pictures to visualize the contents of the map
|
||||
and the pairs stored in the map. We will do this during lecture, and you should do so all the time in practice.
|
||||
|
||||
## 16.2 Typedefs
|
||||
## 16.5 Typedefs
|
||||
|
||||
- One of the painful aspects of using maps is the syntax. For example, consider a constant iterator in a map
|
||||
associating strings and vectors of ints:
|
||||
@@ -65,7 +79,7 @@ map_vect :: const_iterator p;
|
||||
|
||||
The compiler makes the substitution for you.
|
||||
|
||||
## 16.3 Standard Library Sets
|
||||
## 16.6 Standard Library Sets
|
||||
|
||||
- STL sets are ordered containers storing unique “keys”. An ordering relation on the keys, which defaults to
|
||||
operator<, is necessary. Because STL sets are ordered, they are technically not traditional mathematical sets.
|
||||
@@ -79,12 +93,12 @@ you shouldn't use [] to access elements in a set.
|
||||
#include <set>
|
||||
```
|
||||
|
||||
## 16.4 Leetcode Exercises (Maps)
|
||||
## 16.7 Leetcode Exercises (Maps)
|
||||
|
||||
- [Leetcode problem 49: Group Anagrams](https://leetcode.com/problems/group-anagrams/). Solution: [p49_group_anagrams.cpp](../../leetcode/p49_group_anagrams.cpp).
|
||||
- [Leetcode problem 290: Word Pattern](https://leetcode.com/problems/word-pattern/). Solution: [p290_word_pattern.cpp](../../leetcode/p290_word_pattern.cpp).
|
||||
|
||||
## 16.5 Leetcode Exercises (Sets)
|
||||
## 16.8 Leetcode Exercises (Sets)
|
||||
|
||||
- [Leetcode problem 414: Third Maximum Number](https://leetcode.com/problems/third-maximum-number/). Solution: [p414_third_max_number.cpp](../../leetcode/p414_third_max_number.cpp).
|
||||
- [Leetcode problem 1207: Unique Number of Occurrences](https://leetcode.com/problems/unique-number-of-occurrences/). Solution: [p1207_unique_number_occurrences.cpp](../../leetcode/p1207_unique_number_occurrences.cpp).
|
||||
|
||||
69
lectures/optimization/map_erase/README.md
Normal file
69
lectures/optimization/map_erase/README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# `erase by iterator` vs. `erase by key` in C++
|
||||
|
||||
## Overview
|
||||
|
||||
erasing by iterator in std::maps is typically faster than erasing by key.
|
||||
|
||||
## Erasing by Iterator:
|
||||
|
||||
- When you erase an element using an iterator, the map already has a direct reference to the node that contains the element to be erased.
|
||||
|
||||
- The iterator is a pointer to the element (node) in the underlying tree structure (which is usually a red-black tree for std::map).
|
||||
|
||||
## Erasing by Key:
|
||||
|
||||
- When you erase an element by key, std::map first has to search for the key.
|
||||
|
||||
- It does this by traversing the red-black tree to find the node that matches the key.
|
||||
|
||||
- After finding the node, it performs the same operation as erase(iterator), but since it had to perform a search to locate the key, this is slower than directly accessing the iterator.
|
||||
|
||||
## Benchmarks
|
||||
|
||||
Compile and run these 2 programs to see the performance difference.
|
||||
|
||||
[map_erase_slow.cpp](map_erase_slow.cpp) [map_erase_fast.cpp](map_erase_fast.cpp)
|
||||
|
||||
Both programs just create a map containing 1 millions integers, and then erase these 1 millions integers. As can be seen from the following results, erasing by iterators is much faster than erasing by key.
|
||||
|
||||
```console
|
||||
$g++ map_erase_slow.cpp -o map_erase_slow
|
||||
$g++ map_erase_fast.cpp -o map_erase_fast
|
||||
|
||||
$time ./map_erase_slow
|
||||
|
||||
real 0m0.640s
|
||||
user 0m0.624s
|
||||
sys 0m0.016s
|
||||
|
||||
$time ./map_erase_fast
|
||||
|
||||
real 0m0.406s
|
||||
user 0m0.386s
|
||||
sys 0m0.020s
|
||||
|
||||
$time ./map_erase_fast
|
||||
|
||||
real 0m0.382s
|
||||
user 0m0.374s
|
||||
sys 0m0.008s
|
||||
|
||||
$time ./map_erase_slow
|
||||
|
||||
real 0m0.629s
|
||||
user 0m0.617s
|
||||
sys 0m0.012s
|
||||
|
||||
$time ./map_erase_slow
|
||||
|
||||
real 0m0.632s
|
||||
user 0m0.623s
|
||||
sys 0m0.009s
|
||||
|
||||
$time ./map_erase_fast
|
||||
|
||||
real 0m0.383s
|
||||
user 0m0.366s
|
||||
sys 0m0.017s
|
||||
|
||||
```
|
||||
20
lectures/optimization/map_erase/map_erase_fast.cpp
Normal file
20
lectures/optimization/map_erase/map_erase_fast.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
int main() {
|
||||
const int N = 1000000;
|
||||
std::map<int, int> m;
|
||||
|
||||
// fill the map with some values
|
||||
for (int i = 0; i < N; ++i) {
|
||||
m[i] = i;
|
||||
}
|
||||
|
||||
// erase all elements using iterators
|
||||
for (std::map<int, int>::iterator itr = m.begin(); itr != m.end(); ) {
|
||||
itr = m.erase(itr); // erase using iterator and move to the next element
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
20
lectures/optimization/map_erase/map_erase_slow.cpp
Normal file
20
lectures/optimization/map_erase/map_erase_slow.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
int main() {
|
||||
const int N = 1000000;
|
||||
std::map<int, int> m;
|
||||
|
||||
// fill the map with some values
|
||||
for (int i = 0; i < N; ++i) {
|
||||
m[i] = i;
|
||||
}
|
||||
|
||||
// erase by key
|
||||
for (int i = 0; i < N; ++i) {
|
||||
m.erase(i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
72
lectures/optimization/map_insert/README.md
Normal file
72
lectures/optimization/map_insert/README.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Using Move Constructors to Improve Performance in std::map::insert
|
||||
|
||||
When working with std::map, understanding how move semantics improve performance can be crucial for optimizing memory management and overall efficiency. The move constructor is an essential tool in this process, especially in the context of inserting elements into a std::map.
|
||||
|
||||
## Basic Overview of std::map Insertions
|
||||
|
||||
A std::map is a sorted associative container that stores key-value pairs. The keys in a map are unique, and they are automatically sorted according to a comparison function. When you insert elements into a map using insert or emplace, the container must allocate memory and potentially rearrange internal data structures to maintain the map's properties.
|
||||
|
||||
The insert operation typically involves:
|
||||
|
||||
- Creating a new key-value pair.
|
||||
- Inserting it into the internal tree structure (typically a Red-Black tree).
|
||||
- If an object is inserted into a map, it may need to be copied or moved, depending on the availability of move semantics and the state of the object being inserted.
|
||||
|
||||
## The Role of Move Constructors
|
||||
|
||||
- Move constructors provide a mechanism to transfer ownership of resources (like dynamically allocated memory) from one object to another, without the overhead of copying the data. In contrast, the copy constructor performs a deep copy, which involves duplicating all resources of the original object. For large objects, this can be slow and memory-intensive.
|
||||
|
||||
- A move constructor transfers the ownership of resources without allocating new memory, which is a much faster process compared to copying.
|
||||
|
||||
## How Move Constructors Improve std::map::insert Performance
|
||||
|
||||
When you insert an element as a a temporary object into a std::map using insert, the insert function will call the copy constructor; but if a move constructor exists, it will call the move constructor instead of the copy constructor. Since in the move constructor, a shallow copy occurs, it is faster than the copy constructor, which normally does a deep copy.
|
||||
|
||||
## Benchmarks
|
||||
|
||||
Compile and run these 2 programs to see the performance difference.
|
||||
|
||||
[map_insert_copy.cpp](map_insert_copy.cpp) [map_insert_move.cpp](map_insert_move.cpp)
|
||||
|
||||
Both programs just create a map and insert 10 elements (where the value is a large class object), the only difference between these two programs, is that the second program defines the move constructor where the first program does not define the move constructor. As can be seen from the following results, having the move constructor will improve the performance of the program.
|
||||
|
||||
```console
|
||||
$g++ -o map_insert_copy map_insert_copy.cpp
|
||||
$g++ -o map_insert_move map_insert_move.cpp
|
||||
|
||||
$time ./map_insert_copy
|
||||
|
||||
real 0m0.507s
|
||||
user 0m0.247s
|
||||
sys 0m0.259s
|
||||
|
||||
$time ./map_insert_copy
|
||||
|
||||
real 0m0.505s
|
||||
user 0m0.208s
|
||||
sys 0m0.297s
|
||||
|
||||
$time ./map_insert_move
|
||||
|
||||
real 0m0.290s
|
||||
user 0m0.178s
|
||||
sys 0m0.112s
|
||||
|
||||
$time ./map_insert_move
|
||||
|
||||
real 0m0.277s
|
||||
user 0m0.176s
|
||||
sys 0m0.101s
|
||||
|
||||
$time ./map_insert_copy
|
||||
|
||||
real 0m0.509s
|
||||
user 0m0.248s
|
||||
sys 0m0.261s
|
||||
|
||||
$time ./map_insert_move
|
||||
|
||||
real 0m0.282s
|
||||
user 0m0.182s
|
||||
sys 0m0.100s
|
||||
```
|
||||
41
lectures/optimization/map_insert/map_insert_copy.cpp
Normal file
41
lectures/optimization/map_insert/map_insert_copy.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class LargeClass {
|
||||
public:
|
||||
std::vector<int> data;
|
||||
std::string name;
|
||||
|
||||
LargeClass(int size, const std::string& n) : data(size), name(n) {
|
||||
// simulate work in the constructor
|
||||
for (int i = 0; i < size; ++i) {
|
||||
data[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
// define a copy constructor for demonstration purposes
|
||||
LargeClass(const LargeClass& other) : data(other.data), name(other.name) {
|
||||
// std::cout << "Copy constructor called\n";
|
||||
}
|
||||
|
||||
// for demonstration, printing the object contents
|
||||
void print() const {
|
||||
std::cout << name << ": " << data[0] << "..." << data[data.size() - 1] << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
const int num_elements = 10000000;
|
||||
const int map_size = 10;
|
||||
|
||||
std::map<int, LargeClass> map_insert;
|
||||
|
||||
for (int i = 0; i < map_size; ++i) {
|
||||
map_insert.insert({i, LargeClass(num_elements, "Insert_" + std::to_string(i))});
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
46
lectures/optimization/map_insert/map_insert_move.cpp
Normal file
46
lectures/optimization/map_insert/map_insert_move.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class LargeClass {
|
||||
public:
|
||||
std::vector<int> data;
|
||||
std::string name;
|
||||
|
||||
LargeClass(int size, const std::string& n) : data(size), name(n) {
|
||||
// simulate work in the constructor
|
||||
for (int i = 0; i < size; ++i) {
|
||||
data[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
// define a copy constructor for demonstration purposes
|
||||
LargeClass(const LargeClass& other) : data(other.data), name(other.name) {
|
||||
// std::cout << "Copy constructor called\n";
|
||||
}
|
||||
|
||||
// define a move constructor
|
||||
LargeClass(LargeClass&& other) noexcept : data(std::move(other.data)), name(std::move(other.name)) {
|
||||
// std::cout << "Move constructor called\n";
|
||||
}
|
||||
|
||||
// for demonstration, printing the object contents
|
||||
void print() const {
|
||||
std::cout << name << ": " << data[0] << "..." << data[data.size() - 1] << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
const int num_elements = 10000000;
|
||||
const int map_size = 10;
|
||||
|
||||
std::map<int, LargeClass> map_insert;
|
||||
|
||||
for (int i = 0; i < map_size; ++i) {
|
||||
map_insert.insert({i, LargeClass(num_elements, "Insert_" + std::to_string(i))});
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user