cs106b midterm review · 2018-02-14 · midterm logistics thursday february 15th, 7:00-9:30 pm...
TRANSCRIPT
CS106B Midterm ReviewAnna Zeng and Aleksander Dash
Slides adapted from Anton Apostolatos, Ashley Taylor and Nolan HandaliSpecial thanks to Brahm Capoor for everything recursion-related
Today’s Session Overview
● Logistics● C++ basics ● ADTs● Big-O● Recursion
○ Tips, tricks, and everything in between!
● Memory, pointers, and link nodes● Additional study tips
Logistics
Midterm Logistics
● Thursday February 15th, 7:00-9:30 pm● Memorial Auditorium● Open book
○ No electronic copies, no print-outs○ There will be a few copies in the front if you don’t have a copy
● Closed notes (reference sheet will be provided)● Pencils highly recommended, pens accepted● Problems graded based on functionality, not style
○ But if we can’t read your handwriting, we can’t grade you!
What’s on the midterm
● Topics:○ C++ basics: Console I/O, strings, streams, functions and pass by
value/reference○ ADTs: Vector, Grid, Stack, Queue, Set (+ HashSet), Map (+ HashMap)○ Big-Oh Notation○ Recursion○ Backtracking○ Pointers
● 8-10 questions total● Mix between reading code and writing code
Source: XKCD
What’s not on the midterm
C++ Basics
Understand what happens when you pass by value vs. reference
Make sample mystery code and test yourself and your friends
Review assignments 1 and 2 for I/O, strings and file streams
Tip #0:
Tip #1:
Source: International Obfuscated C Code Contest
Tip #2:
Tracing Exerciseint main() {
int frodo = 7;
int sam = 5;
int merry = 4;
int pippin = cervantes(sam, dumas(frodo, sam, merry));
cout << sam << endl;
cout << pippin << endl;
cout << merry << endl;
return 0;
}
int cervantes(int &sancho, int quixote) {
sancho *= quixote;
return quixote--;
}
int dumas(int athos, int &aramis, int &porthos) {
if (athos + aramis < porthos) {
athos = aramis - porthos;
} else {
porthos--;
}
return aramis - (athos + 7);
}
Tracing Exerciseint main() {
int frodo = 7;
int sam = 5;
int merry = 4;
int pippin = cervantes(sam, dumas(frodo, sam, merry));
cout << sam << endl;
cout << pippin << endl;
cout << merry << endl;
return 0;
}
int cervantes(int &sancho, int quixote) {
sancho *= quixote;
return quixote--;
}
int dumas(int athos, int &aramis, int &porthos) {
if (athos + aramis < porthos) {
athos = aramis - porthos;
} else {
porthos--;
}
return aramis - (athos + 7);
}
Console
-45-93
ADTs
Vector(1D list of elements)
43 12 0 -20 32
Grid(2D list of elements)
4 90 20
12 0 33
Stack(LIFO linear structure)
43
21
-1
89
Queue(FIFO linear structure)
push pop
3 0 -1 78enqueue dequeue
Map(Collection of key/value pairs)
Set(Collection of unique elements)
ADT Exercise I
Write a function that reads a input file of phone numbers and prints all the phone numbers with the most commonly occurring area code.
Sample input.txt:650-723-2273206-685-2181800-356-9377800-347-3288650-725-7411520-297-6312206-543-1695800-266-2278206-543-2969
Sample output:206-543-1695 206-543-2969 206-685-2181
void areaCodes(string filename) {
// open file ifstream input;
ifstream input(filename);
input.open(filename.c_str());
if (input.fail()) {
return;
}
// read file data into map of sets
Map <string, Set<string>> numbers;
string line;
while (getline(input, line)) {
string areaCode = line.substr(0, 3);
numbers[areaCode].add(line);
}
Solution (part 1)
Solution (part 2) // find most popular area code
string best = "";
for (string areaCode : numbers) {
if (best.empty() || numbers[areaCode].size() > numbers[best].size()) {
best = areaCode;
}
}
// print all numbers in that area code
for (string number : numbers[best]) {
cout << number << endl;
}
}
ADT Exercise IIWrite a function
commonContacts(Map<string, int>& myBook, Map<string, int>& theirBook)
that returns a set of the names of all the common contacts (same name & number) between two phonebooks
“Chris”“Marty”“Mehran”
“685-9138”“546-0166”“950-3107”
ADT Exercise II - Using vectors
Set<string> commonContacts(Map<string, int>& myBook, Map<string, int>& theirBook) {
Set<string> commonContacts;
for (string myContact : myBook.keys()) {
for (string theirContact : theirBook.keys()) {
if (myContact == theirContact && myBook[myContact] == theirBook[theirContact]) {
commonContacts.add(myContact);
}
}
}
return commonContacts;
}
Big-Oh Notation
Big-Oh Exercise I
int mystery(Vector<int>& vec, Set<int>& database) {
int total = 0;
Set<int> s = database;
for (int itemOne : vec) {
for (int itemTwo : database) {
if (itemOne == itemTwo) {
total++;
} else {
for (int itemThree : vec) {
s.add(itemThree);
}
}
}
}
return total;
}
If vec has N elements and database has M elements:
Big-Oh Exercise I - Answer: O(N²Mlog(M))
int mystery(Vector<int>& vec, Set<int>& database) {
int total = 0; // O(1)
Set<int> s = database; // O(M)
for (int itemOne : vec) { // O(N * M * N * log(M))
for (int itemTwo : database) { // O(M * N * log(M))
if (itemOne == itemTwo) { // O(1)
total++; // O(1)
} else {
for (int itemThree : vec) { // O(N * log(M))
s.add(itemThree); // O(log(M))
}
}
}
}
return total; // O(1)
}
If vec has N elements and database has M elements:
Big-Oh Exercise II
int recurse(Stack<int>& s) {
if (!s.isEmpty()){
int x = s.pop();
return x + recurse(s);
}
return 0;
}
If s has N elements:
Big-Oh Exercise II - Answer: O(N)
int recurse(Stack<int>& s) {
if (!s.isEmpty()){
int x = s.pop(); // O(1)
return x + recurse(s); // O(1)
}
return 0;
}
If s has N elements: 1. What is the Big-Oh of each recursive call?
2. How many recursive calls do we make?
Each recursive step is O(1), and since we go through the entire stack one element at a time, we have N recursive steps, so we multiply them to get O(1 * N)
RecursionBrahm Capoor
In order to understand recursion, you must first understand recursion
Section problems
1. Problem 3: Reverse2. Problem 5: Subsequence
Reverse
● Let’s find the base case○ When is the problem so simple you don’t need to think? ○ What’s the simplest possible string?
● Now let’s do the harder cooler part○ If we have a more complex string, how can we make the problem
simpler?○ reverse(“banter”) = “retnab”○ key insight: we can break a string into two parts: the first letter,
and the rest of the string○ How does this suggest recursion?
The Pseudocode
function reverse(s):if s is blank:
return a blank stringelse:
c = first character of sr = rest of stringreturn reverse(r) + c
The Codestring reverse(string s) {
if (s == “”) return “”;return reverse(s.substr(1)) + s[0];
}
Subsequence: is s2 a subsequence of s1?● Let’s find the base case(s)
○ When is the problem so simple that you don’t need to think?○ What’s the simplest possible string? ○ How many base cases do we need?
● Now let’s do the harder cooler part○ If we have more complex strings, how do we make the problem simpler?○ examples:
■ isSubsequence(“catch”, “cat”) = isSubsequence(“atch”, “at”)■ isSubsequence(“scatter”, “cat”) = isSubsequence(“catter”, “cat”)
○ key insights:■ if the two strings have the same first character, we examine the
rest of the strings■ if the two strings have different first characters, we examine
the rest of the first string
The Pseudocode
function is_subseq(s1, s2) :if s2 is blank:
return trueif s1 is blank:
return falseelse:
if same first character:r1 = rest of s1r2 = rest of s2return is_subseq(r1, r2)
else:r1 = rest of s1return is_subseq(r1, s2)
The Codebool is_subseq(string s1, string s2) {
if (s2 == “”) return true;if (s1 == “”) return false;if (s1[0] == s2[0]){
string r1 = s1.substr(1);string r2 = s2.substr(1);return is_subseq(r1, r2);
} else {string r1 = s1.substr(1);return is_subseq(r1, s2);
}}
Other useful recursion problems
● Tracing problem: ○ Problem 1
● Basic recursion practice:○ Problem 2: Sum of squares
● Recursion string problems:○ Problem 3: Reverse○ Problem 4: Star string
Backtracking
Choose. Explore. Unchoose. Repeat.
Recursion vs backtracking: building an intuition
bool is_subseq(string s1, string s2) {if (s2 == “”) return true;if (s1 == “”) return false;if (s1[0] == s2[0]){
string r1 = s1.substr(1);string r2 = s2.substr(1);return is_subseq(r1, r2);
} else {string r1 = s1.substr(1);return is_subseq(r1, s2);
}}
string reverse(string s) {if (s == “”) return “”;return reverse(s.substr(1)) + s[0];
}
Recursion vs backtracking: building an intuition
bool is_subseq(string s1, string s2) {if (s2 == “”) return true;if (s1 == “”) return false;if (s1[0] == s2[0]){
string r1 = s1.substr(1);string r2 = s2.substr(1);return is_subseq(r1, r2);
} else {string r1 = s1.substr(1);return is_subseq(r1, s2);
}}// the recursive cases are mutually exclusive
string reverse(string s) {if (s == “”) return “”;return reverse(s.substr(1)) + s[0];
}
● In recursion, you only ever do one recursive call at every level of the recursion
● In recursion, you know that your recursive call will work (it’s the leap of faith!)
Section problems
1. Problem 4: Longest Common Subsequence
isSubsequence
bool is_subseq(string s1, string s2) {if (s2 == “”) return true;if (s1 == “”) return false;if (s1[0] == s2[0]){
string r1 = s1.substr(1);string r2 = s2.substr(1);return is_subseq(r1, r2);
} else {string r1 = s1.substr(1);return is_subseq(r1, s2);
}}
● Let’s find a base case○ When is the problem so simple
you don’t need to think?○ What’s the simplest possible
string?● Let’s think about more complicated
versions of the problem● Let’s try and mirror the structure of
bool is_subseq(string s1, string s2)○ What’s the LCS when the two
strings have the same first character?
○ What’s the LCS when the two strings have different first characters?
subseq(“banter”, “bar”) = subseq(“anter”, “ar”)subseq(“banter”, “ant”) = subseq(“anter”, “ant”)
LCS redux: so why backtracking?
● Let’s find a base case○ When is the problem so simple
you don’t need to think?○ What’s the simplest possible
string? when strings are blank!● Let’s think about more complicated
versions of the problem● Let’s try and mirror the structure of
bool is_subseq(string s1, string s2)○ What’s the LCS when the two
strings have the same first character? LCS(the rest)
○ What’s the LCS when the two strings have different first characters?
bool is_subseq(string s1, string s2) {if (s2 == “”) return true;if (s1 == “”) return false;if (s1[0] == s2[0]){
string r1 = s1.substr(1);string r2 = s2.substr(1);return is_subseq(r1, r2);
} else {string r1 = s1.substr(1);return is_subseq(r1, s2);
}}
subseq(“banter”, “bar”) = subseq(“anter”, “ar”)subseq(“banter”, “ant”) = subseq(“anter”, “ant”)
LCS redux: so why backtracking?
● What do we do when the two strings have different first
characters?○ LCS(“banter”, “ant”) = LCS(“anter”, “ant”)
■ How did I know to remove the “b” from “banter”?○ LCS(“banter”, “abat”) = LCS(“banter”, “bat”)
■ How did I know to remove the “a” from “abat”?
● Radical idea: What if I didn’t know? ● What if I tried both ways?
○ I’d get two subsequences. How would I know which was better? ■ Hint: What is the function trying to find?
○ Let’s put it all together...
The Pseudocode
function LCS(s1, s2) :if s1 or s2 is blank:
return a blank stringelse:
if same first character:c = first charr1 = rest of s1r2 = rest of s2return c + LCS(r1, r2)
else:r1 = rest of s1r2 = rest of s2p1 = LCS(s1, r2)p2 = LCS(r1, s2)return longer of p1 and p2
Compare: isSubsequence Pseudocode
function is_subseq(s1, s2) :if s2 is blank:
return trueif s1 is blank:
return falseelse:
if same first character:r1 = rest of s1r2 = rest of s2return is_subseq(r1, r2)
else:r1 = rest of s1return is_subseq(r1, s2)
The Pseudocode
function LCS(s1, s2) {if s1 or s2 is blank:
return a blank stringelse:
if same first character:c = first charr1 = rest of s1r2 = rest of s2return c + LCS(r1, r2)
else:r1 = rest of s1r2 = rest of s2p1 = LCS(s1, r2)p2 = LCS(r1, s2)return longer of p1 and p2
The Codestring LCS(string s1, string s2) {
if (s1 == “” || s2 == “”) return “”;if (s1[0] == s2[0]){
string r1 = s1.substr(1);string r2 = s2.substr(1);return s1[0] + LCS(r1, r2);
} else {string r1 = s1.substr(1);string r2 = s2.substr(1);string p1 = LCS(s1, r2);string p2 = LCS(r1, s2);if (p1.length() > p2.length() {
return p1;} else {
return p2;}
}}
Rethinking backtrackingstring LCS(string s1, string s2) {
if (s1 == “” || s2 == “”) return “”;if (s1[0] == s2[0]){
string r2 = s2.substr(1);string r2 = s2.substr(1);return s1[0] + LCS(r1, r2);
} else {string r1 = s1.substr(1);string r2 = s2.substr(1);string p1 = LCS(s1, r2);string p2 = LCS(r1, s2);if (p1.length() > p2.length()) {
return p1;} else {
return p2;}
}}
● It’s hard to think of this in terms of choose/explore/unchoose
● But what did we do?
Rethinking backtrackingstring LCS(string s1, string s2) {
if (s1 == “” || s2 == “”) return “”;if (s1[0] == s2[0]){
string r2 = s2.substr(1);string r2 = s2.substr(1);return s1[0] + LCS(r1, r2);
} else {string r1 = s1.substr(1);string r2 = s2.substr(1);string p1 = LCS(s1, r2);string p2 = LCS(r1, s2);if (p1.length() > p2.length()) {
return p1;} else {
return p2;}
}}
● It’s hard to think of this in terms of choose/explore/unchoose
● But what did we do?○ We found the possible options
Rethinking backtrackingstring LCS(string s1, string s2) {
if (s1 == “” || s2 == “”) return “”;if (s1[0] == s2[0]){
string r2 = s2.substr(1);string r2 = s2.substr(1);return s1[0] + LCS(r1, r2);
} else {string r1 = s1.substr(1);string r2 = s2.substr(1);string p1 = LCS(s1, r2);string p2 = LCS(r1, s2);if (p1.length() > p2.length()) {
return p1;} else {
return p2;}
}}
● It’s hard to think of this in terms of choose/explore/unchoose
● But what did we do?○ We found the possible options○ We figured out what the best
one was
Rethinking backtrackingstring LCS(string s1, string s2) {
if (s1 == “” || s2 == “”) return “”;if (s1[0] == s2[0]){
string r2 = s2.substr(1);string r2 = s2.substr(1);return s1[0] + LCS(r1, r2);
} else {string r1 = s1.substr(1);string r2 = s2.substr(1);string p1 = LCS(s1, r2);string p2 = LCS(r1, s2);if (p1.length() > p2.length()) {
return p1;} else {
return p2;}
}}
● It’s hard to think of this in terms of choose/explore/unchoose
● But what did we do?○ We found the possible options○ We figured out what the best
one was○ We gave that one back
Rethinking backtrackingstring LCS(string s1, string s2) {
if (s1 == “” || s2 == “”) return “”;if (s1[0] == s2[0]){
string r2 = s2.substr(1);string r2 = s2.substr(1);return s1[0] + LCS(r1, r2);
} else {string r1 = s1.substr(1);string r2 = s2.substr(1);string p1 = LCS(s1, r2);string p2 = LCS(r1, s2);if (p1.length() > p2.length()) {
return p1;} else {
return p2;}
}}
● It’s hard to think of this in terms of choose/explore/unchoose
● But what did we do?○ We found the possible options○ We figured out what the best
one was○ We gave that one back
● Backtracking isn’t about choosing, exploring and unchoosing
○ It’s about figuring out which option works best
○ (sometimes, that means choosing, exploring and unchoosing)
That said...string LCS(string s1, string s2) {
if (s1 == “” || s2 == “”) return “”;if (s1[0] == s2[0]){
string r2 = s2.substr(1);string r2 = s2.substr(1);return s1[0] + LCS(r1, r2);
} else {string r1 = s1.substr(1);string r2 = s2.substr(1);string p1 = LCS(s1, r2);string p2 = LCS(r1, s2);if (p1.length() > p2.length()) {
return p1;} else {
return p2;}
}}
● Choose (kinda)
That said...string LCS(string s1, string s2) {
if (s1 == “” || s2 == “”) return “”;if (s1[0] == s2[0]){
string r2 = s2.substr(1);string r2 = s2.substr(1);return s1[0] + LCS(r1, r2);
} else {string r1 = s1.substr(1);string r2 = s2.substr(1);string p1 = LCS(s1, r2);string p2 = LCS(r1, s2);if (p1.length() > p2.length()) {
return p1;} else {
return p2;}
}}
● Choose (kinda)● Explore (kinda)
That said...string LCS(string s1, string s2) {
if (s1 == “” || s2 == “”) return “”;if (s1[0] == s2[0]){
string r2 = s2.substr(1);string r2 = s2.substr(1);return s1[0] + LCS(r1, r2);
} else {string r1 = s1.substr(1);string r2 = s2.substr(1);string p1 = LCS(s1, r2);string p2 = LCS(r1, s2);if (p1.length() > p2.length()) {
return p1;} else {
return p2;}
}}
● Choose (kinda)● Explore (kinda)● Unchoose (kinda-er)
Other useful backtracking problems
● (all of them)● Working with the choose-explore-unchoose paradigm:
○ Problem 7: Print Squares
● Using Recursive helper functions:○ Problem 6: Make Change○ Problem 9: Ways to climb
● Batsh!t awesome problems:○ Problem 10: Twiddle○ Problem 11: Hacking & cracking
● (So literally, all of them. These are awesome midterm practice)
endl;/* good luck with the midterm! */
Pointers and Linked Nodes
Linked Lists 101
int main() {ListNode* front = new ListNode();front->data = 3;delete front;
}● Use new to create a new object on the heap● To access data on the other end of a pointer, use ->
○ Remember that this is just the same as, (*front).data
● Need to delete whatever you create○ Otherwise, memory leaks!
Linked Nodes practice
1list1 2 3
list2 4 5
(8 out of 9 practice midterms have some form of this as the linked list problem)
Tips:● Draw a picture!● Potentially declare temporary pointers to help you rearrange nodes
struct ListNode { int data; ListNode* next; }
ListNode* temp = list1;
ListNode* temp2 = list2;
Linked Nodes practice answer1list1 2 3
list2 4 5
temp
temp2
list1
list2
1 2 3
4 5
temp
temp2
list1 = list1->next;
list2 = list1->next;
list1 -> next = temp2 -> next;
Linked Nodes practice answer
1
list1
2
3list2
4 5
temp
temp2
list1->next->next = temp;
list1->next->next->next = nullptr;
list2 ->next = temp2;
list2->next->next = nullptr;
Pointer trace tips: The * symbol has two uses
● type* ptrName declares a pointer that points to an object of type type ○ e.g. ListNode* front is a pointer that points to a ListNode
● *ptrName dereferences ptrName (aka, goes to where the pointer is pointing to)○ We’ve seen this! Recall that ptr->data is equivalent to (*ptr).data
Pointer trace tips: The & symbol has two uses
● &value means “give me the memory address that value is stored at”○ e.g. &value returns something that looks like 0x20065ab0
● Don’t confuse this with reference parameters in function prototypes!○ e.g. void printList (Vector<int>& v) { ...
Pointer trace practice (from Practice Midterm #9)
What is the output?
Pointer trace practice (from Practice Midterm #9)
What is the output?
Console
203 3005 11 3003 16 204 11 204 3003 16
Additional Study Tips
● Good practice materials:○ Practice exams (9 total)○ Leftover section problems○ Codestepbystep.com
● See Julie Zelenski’s “Exam Strategies” handout (bottom of cs106b.stanford.edu/exams.shtml)
● You got this! Good luck!