Upload
others
View
2
Download
0
Embed Size (px)
Citation preview
CS106B
Autumn 2017
Instructor: Cynthia Lee
December 11, 2017
FINAL EXAM
NAME (LAST, FIRST): _____________________________________________________
SUNET ID: ________________________________________________ @stanford.edu
1 2 3 4 5
Exam extras
TOTAL Heap Inheritance
Recursive
Backtracking
Graphs Trees
18 10 18 16 26 2 90
Instructions:
The time for this exam is 3 hours.
Use of anything other than a pencil, eraser, pen, one 8.5x11 page (both sides) of notes, and the official
textbook is prohibited. In particular, no computers or digital devices of any kind are permitted. For scratch
paper, please just use the blank back sides of each exam page. If part of your solution is on the back of a
page, you MUST clearly indicate this in the space where the answer was supposed to go.
Write your name on each page on the line provided (worth 1pt). You must do this during the exam—
writing your name after time is called will not be permitted.
PLEASE tear off the pages of library reference in the back of the exam and do not turn them in (also 1 pt).
Please sign before you begin:
I agree to abide by the spirit and letter of the Honor Code, and to follow the instructions above.
_______________________________________________ ______________ ______________________
(Signature) (Date) (Start time - HH:MM zone)
1. Heap (18pts). We have implemented the Priority Queue ADT using a binary min-heap.
(a) (10pts) Draw a diagram of the tree shape of the heap after enqueuing the following priorities in the order
given. 15, 10, 13, 8, 2, 9 (for this priority queue we don’t have a separate value, just the priority).
Diagram after inserting 15: This one is completed for you as a node formatting example.
Diagram after inserting 10:
Diagram after inserting 13:
Diagram after inserting 8:
Diagram after inserting 2: Diagram after inserting 9:
15
(b) (4pts) Continuing from the final heap in part (a) (after inserting 9), draw a diagram of the tree shape of
the heap after calling dequeue twice.
Diagram after calling dequeue once: Diagram after calling dequeue a second time:
(c) (4pts) Draw the array version of the heap after the second dequeue above, including the capacity and size
fields, as discussed in class. Leave currently unused parts of the array blank.
Array index
0 1 2 3 4 5 6 7 8 9
Array contents
Capacity:
Size:
2. Inheritance (10pts). Consider the classes on the left; assume that each is defined in its own file.
class Anakin { public: virtual void m1() { cout << "A 1" << endl; } void m3() { cout << "A 3" << endl; m1(); } }; class Leia : public Anakin { public: virtual void m2() { cout << "L 2" << endl; Anakin::m3(); } void m3() { cout << "L 3" << endl; m1(); } }; class Kylo : public Leia { public: virtual void m1() { cout << "K 1" << endl; } virtual void m2() { cout << "K 2" << endl; Leia::m3(); } void m3() { cout << "K 3" << endl; m2(); } }; Now assume that the following variables are defined: Anakin* var1 = new Leia(); Anakin* var2 = new Kylo(); Leia* var3 = new Kylo();
In the table below, indicate in the right-hand column the output produced by the statement in the left-hand column. If the statement produces more than one line of output, indicate the line breaks with slashes as in "x / y / z" to indicate three lines of output with "x" followed by "y" followed by "z".
If the statement does not compile, write "compiler error". If a statement would crash at runtime or cause unpredictable behavior, write "crash".
Statement
var1->m1();
var1->m3();
var2->m1();
var2->m2();
var3->m2();
((Kylo*) var1)->m3();
((Leia*) var2)->m2();
((Kylo*) var2)->m1();
((Kylo*) var3)->m3();
((Anakin*) var3)->m1();
Output (1pt each)
3. Recursive Backtracking (18pts). The game of Battleship is a time-honored
competition amongst friends. Each person has a board (which we’ll represent with
a Grid) where they secretly place several “ships” (1xN rectangles) so that they do
not overlap other ships or go off the board. The picture to the right is an example
of a Grid with 4 ships placed on it: size 3 (placed horizontally), size 2 (placed
vertically), and two of size 1. Each cell with ‘B’ represents part of a ship, and
complete ships are outlined in black.
To win, you try to “sink” your friend’s ship by naming a row/col location to target with a cannon. Your friend
self-reports whether you “hit” on a part of one of their ships or not (“miss”). If the locations you name result
in many consecutive misses, you might begin to wonder whether your opponent is cheating in their self-
reporting! So you decide to write a backtracking recursion program to determine whether there’s any legal
way to place the ships that avoids all the locations you’ve targeted so far.
(a) (6pts) First, you’ll need a helper function placeHoriz that attempts to place one
ship on the board in a specified location. We represent your friend’s board (from
your perspective) as a Grid<char>, where ‘?’ represents a spot you know nothing
about, ‘M’ represents a location you have already targeted (and that your friend
said was a miss), and ‘B’ represents a placed ship (placed tentatively as part of
backtracking exploration). The function takes the current Grid, the length of the
ship, and a row-col where you should place the ship. As the function’s name
suggests, you should try to place the ship horizontally on the board with the
leftmost part of the ship at row and col. If the ship fits (does not overlap any ‘M’ or
‘B’ cells, and stays in bounds of the board), fill in the designated cells with ‘B’ (to
indicate a tentative guess at a possible ship placement) and return true. Otherwise
return false. If your function returns false, no changes should be made to the board.
bool placeHoriz (Grid<char>& board, int size, int row, int col){
}
Before:
After (with size=2,
row=2, col=1):
(b) (12pts) Now write a recursive backtracking function canPlaceShips that checks if a collection of ships
can all be placed on the board such that they do not overlap each other or any cell marked ‘M.’ The
collection of ships is provided as a Vector<int> of sizes, where each int represents one ship’s size. The
function returns true if it’s possible to place all the ships on the board, and false otherwise.
Example: It would be impossible to place four ships of sizes 3, 2, 1, and 1 in any configuration on
the “before” example board in part (a)—something fishy (ha) is going on with your friend’s self-
reporting!—so you would return false in that case.
You’ll want to use your placeHoriz helper function, and you may assume a corresponding
placeVert also exists, which does the same except that it places the ship vertically.
You may also assume you have helpers and unplaceHoriz and unplaceVert, which remove a
ship of size size from the specified location by writing ‘?’ in all its cells (the ‘unplace’ functions
have the same input parameter list as the ‘place’ functions, but void return type).
Your function should use backtracking recursion. Your code is not required to have any particular
Big-O cost, but you may lose points if your code is extremely inefficient, such as exploring
obviously invalid paths rather than stopping and backtracking.
bool canPlaceShips(Grid<char>& board, Vector<int> shipSizes){
}
4. Graphs (16pts). Noticing the popularity of the Stanford Marriage Pact, Stanford’s housing office has decided
to use a similar algorithm to match roommates next year. They want to force students currently in Roble and
Wilbur to mix next year, so each roommate pair must include one student from each. Based on a proprietary
deep learning algorithm that examines your social media and grades (serious privacy issues in this
hypothetical!), ResEd has created a weighted, directed graph of all students in R[oble] and W[ilbur], where an
edge exists from each student in R to each student in W, with weight indicating how much the R student
should want to match with the W student. Corresponding reverse edges (with potentially different weights)
exist from W students to R students. Note that smaller weights indicate more desirable matches, and R and
W have the same number of students. You will write a function to help ResEd create roommate matches!
(a) (5pts) Before we tackle the main algorithm, write a helper function that takes a BasicGraph of the
roommate ratings and returns a Map from W students (vertices colored WHITE) to a PriorityQueue of
R students (vertices colored RED), where the priority is the weight of the edge from the W student to that
R student. (Assume ints WHTIE and RED are already defined appropriately as constants.)
Map<Vertex*, PriorityQueue<Vertex*>> getWPrefs(BasicGraph& graph) {
}
(b) (11pts) Now we will write the main algorithm:
Some of this code is written for you (see next page).
Variable wPrefs is the return value of your helper in (a), isMatched tracks which W students already
have matches, and matches map each R student to their current W student match.
The algorithm repeats the following actions in a loop until all students have a match:
Loop over wPrefs keys and, for each W student that is not already matched, attempt to match them
with the next highest-priority roommate for them (next in W’s PriorityQueue):
o A match attempt will succeed if the R student is currently not matched to anyone.
o A match attempt will also succeed if the R student is currently matched to someone else, but the
R student would prefer this new W student to their current match (i.e., the new edge weight is less
than the edge weight with the current match).
o To perform a match, update isMatched and matches accordingly, including breaking the
previous match if the R student had one.
o If the first match attempt for W fails, W stays unmatched this round. Continue the loop to the next
W student in wPrefs.
Return matches.
Please write your code on the next page.
Map<Vertex*, Vertex*> matchRoommates(BasicGraph& graph) {
Map<Vertex*, PriorityQueue<Vertex*>> wPrefs = getWPrefs(graph); // W -> PQ<R>
Set<Vertex*> isMatched; // W vertices that are currently matched
Map<Vertex*, Vertex*> matches; // from R to W
while (isMatched.size() < wPrefs.size()) {
for (Vertex* w : wPrefs) {
} } return matches; }
(b) Trees (26pts). A k-ordered statistic tree is a Binary Search Tree where each node has an additional field
that stores the number of nodes in its left subtree. The k-ordered statistic tree can use this information
to quickly locate the kth element in the tree (kth if all elements were listed in ascending sorted order). We
will use this to implement a Set ADT, so keys in the tree are all unique.
Example: Below is a valid k-ordered statistic tree. Notice the keys follow the usual BST ordering.
The file korder.h is as follows (do not edit this code):
struct Node { Node(int key) { this->key = key; count = 0; left = right = NULL; } int key; // the usual BST key
int count; // count of nodes in left subtree Node* left; // left child Node* right; // right child
}; class KTree { public: KTree(); ~KTree(); void addKey(int key); int getKthKey(int k); private:
Node* root; };
Functions in the korder.cpp file are shown below and on the following 2 pages. Complete the code for them. You are welcome to add additional helper function(s) if you want (do not add them to the class in .h file, just add them below).
// (2pts) Constructor KTree::KTree()
{
}
// (6pts) Destructor KTree::~KTree()
{
}
// (7pts) Inserts key into the tree in the proper place, and updates all tree // counts appropriately. Your solution must be recursive, using the provided // helper. void KTree::addKey(int key) {
}
// Recursive helper function for addKey. Returns true if node was added, false // if key was duplicate so no add was done. The code for a standard BST insert // is already provided here for you. You should edit this code to make it // work for k-ordered tree. Write your additional line(s) of code to the right // and use arrows to indicate where to insert your addition(s). Cross out any // code you want to delete. bool addKeyHelper(int key, Node* curr)
{
if (key < curr->key) {
if (curr->left == NULL) {
curr->left = new Node(key);
return true;
} else {
return addKeyHelper(curr->left);
}
} else if (key > curr->key) {
if (curr->right == NULL) {
curr->right = new Node(key);
return true;
} else {
return addKeyHelper(curr->right);
}
} else {
return false;
}
}
// (11pts) Returns the kth smallest key in the tree (numbered starting // with 0). In the example tree above, for k=0 return 2; k=3 return 11; k=5 // return 20. You may assume that is valid (0 <= k < N for tree with N nodes). // Your solution must be recursive (see recursive helper below). For full // credit, your solution must be O(logN). As a fallback option, O(N) solutions // that do not use auxiliary data structures will incur a small 2pt deduction. int KTree::getKthKey(int k)
{
}
// Recursive helper for getKthKey(). For the O(N) solution, you may add
// argument(s) and change the return value if you like. For the O(logN)
// solution, this is the best header.
int kthKeyHelper(int k, Node* curr )
{
}
Summary of Relevant Data Types
We tried to include the most relevant member functions for the exam, but not all member functions are listed.
You are free to use ones not listed here that you know exist. You do not need #include.
class string {
bool empty() const; // O(1)
int size() const; // O(1)
int find(char ch) const; // O(N)
int find(char ch, int start) const; // O(N)
string substr(int start) const; // O(N)
string substr(int start, int length) const; // O(N)
char& operator[](int index); // O(1)
const char& operator[](int index) const; // O(1)
};
class Vector {
bool isEmpty() const; // O(1)
int size() const; // O(1)
void add(const Type& elem); // operator+= used similarly – O(1)
void insert(int pos, const Type& elem); // O(N)
void remove(int pos); // O(N)
Type& operator[](int pos); // O(1)
};
class Grid {
int numRows() const; // O(1)
int numCols() const; // O(1)
bool inBounds(int row, int col) const; // O(1)
Type get(int row, int col) const; // cascade of operator[] also works – O(1)
void set(int row, int col, const Type& elem); // O(1)
};
class Stack {
bool isEmpty() const; // O(1)
void push(const Type& elem); // O(1)
Type pop(); // O(1)
};
class PriorityQueue {
void changePriority(ValueType value, double newPriority);
void clear();
ValueType dequeue();
void enqueue(const ValueType& value, double priority); ValueType peek() const;
int size() const;
};
class Queue {
bool isEmpty() const; // O(1)
void enqueue(const Type& elem); // O(1) Type dequeue(); // O(1)
};
class Map {
bool isEmpty() const; // O(1)
int size() const; // O(1) void put(const Key& key, const Value& value); // O(logN)
bool containsKey(const Key& key) const; // O(logN)
Value get(const Key& key) const; // O(logN)
Value& operator[](const Key& key); // O(logN)
};
Example for loop: for (Key key : mymap){…}
class HashMap {
bool isEmpty() const; // O(1)
int size() const; // O(1)
void put(const Key& key, const Value& value); // O(1)
bool containsKey(const Key& key) const; // O(1) Value get(const Key& key) const; // O(1)
Value& operator[](const Key& key); // O(1)
};
Example for loop: for (Key key : mymap){…}
class Set {
bool isEmpty() const; // O(1)
int size() const; // O(1)
void add(const Type& elem); // operator+= also adds elements – O(logN)
bool contains(const Type& elem) const; // O(logN)
void remove(ValueType value);
}; Example for loop: for (Type elem : mymap){…}
class Lexicon { int size() const; // O(1) bool isEmpty() const; // O(1) void clear(); // O(N) void add(string word); // O(W) where W is word.length() bool contains(string word) const; // O(W) where W is word.length() bool containsPrefix(string pre) const; // O(W) where W is pre.length() };
Example for loop: for (string str : english){…}
struct Edge {
Vertex* start;
Vertex* finish; double weight;
bool visited;
};
struct Vertex {
string name; Set<Edge*> arcs;
Set<Edge*>& edges;
bool visited;
Vertex* previous;
int getColor();
};
class BasicGraph : public Graph<Vertex, Edge> {
public:
Vertex* addVertex(Vertex* v);
Edge* getEdge(Vertex* v1, Vertex* v2) const;
Edge* getEdge(string v1, string v2) const; const Set<Edge*>& getEdgeSet() const;
const Set<Edge*>& getEdgeSet(Vertex* v) const;
const Set<Edge*>& getEdgeSet(string v) const;
const Set<Vertex*> getNeighbors(Vertex* vertex) const;
const Set<Vertex*> getNeighbors(string vertex) const;
Vertex* getVertex(string name) const;
const Set<Vertex*>& getVertexSet() const;
void removeEdge(string v1, string v2, bool directed = true);
void removeEdge(Vertex* v1, Vertex* v2, bool directed = true);
void removeEdge(Edge* e, bool directed = true);
void removeVertex(string name);
void removeVertex(Vertex* v); void resetData();
}