Part 1. 4 points each x 5 = 20 points General idea: 2 points per part for each A: (2+3)*4 4*(2+3) either is worth 2 points. Must have parentheses. This is all or nothing, no +1 for "close" Why better? +2 Answer: better because of precedence/order of operations, no parantheses needed. Must say something related to that to get these points, no partial. B: Avoids clustering, avoids problems with deleted values, copes better with full tables (long chains are ok, but degrade performance,a full probing table requires rehashing). +4 for reasonable description of any one (preferably two, but good description of clustering is enough). Lose points for saying incorrect things C: Identify complexity of both loops and combine to yield O(n log n) +3 for each piece (+1 and +1, respectively for each loop, +1 for one expression or getting average/worst right) +1 for overall answer, combining all expressions void sort(tvector& v) { tpqueue pq; for(int k=0; k < v.size(); k++){ // this is O(n) average pq.insert(v[k]); // O(n log n) worst } for(int k=0; k < v.size(); k++){ // this is O(n log n) pq.deletemin(v[k]); } } D: Define stable correctly in terms of leaving relative order of secondary keys unchanged. No credit for anything other than correct definition. If definition is pretty hazy, but identiable, then +3 is ok. But mostly +4/+0 E: Purpose is to use a function object to sort in reverse, high to low. +2 identify sort as high to low +2 identify function object as the vehicle used to sort (don't need to call it a function object) ------ 2 A. Here's the solution: if (a.front() < b.front()){ a.dequeue(item); } else { b.dequeue(item); } They must use front to get full credit: -2 if they call dequeue no matter what, e.g., a.dequeue(aitem); b.dequeue(bitem); if (aitme < bitem) ... -1 for syntax errors with dequeue(item), e.g., storing return value item = a.dequeue() -1 extraneous/other syntax problems Part B: Explain why T(n) = 2T(n/2) + O(n) and thus O(n log n) The O(n) with explanation is +1, the 2T(n/2) is +1, the solution is +1 (with no justification) and an explanation, e.g., "like mergesort" is +1 --- +2 explain generally how it works +2 explain why stack can't work, e.g., to get to first element you must pop the other ones, where do they go? Generous on these points, sometimes people lost the first +2 because they didn't explain how the code works, or because they confused stacks and queues and only talked about queues. Part C: void filterQ(tqueue& q, Predicate& pred) { int size = q.size(); string item; for(int k=0; k < size; k++){ q.dequeue(item); if (! pred.satisfies(item)){ q.enqueue(item); } } } +1/-1 Set size before loop, cannot do in loop test, e.g., q.size() since size can change +1/-1 Use !pred.satisfies(item) (look for !) +1/-1 general structure with loop over items, calling dequeue/enqueue +1/-1 syntax problems iwth queue or pred.satisfies Part D: struct VowelStarter : public Predicate { bool satisfies(const string& arg) { string vowels = "aieou"; return vowels.find(arg[0]) != string::npos; } } +1/-1 missing : public Predicate +1/-1 missing bool satisfies header +1/-1 have return that works for both true/false correctly) (sometimes backwards, or missing one return value) +1/-1 other problems, or extra garbage in the struct ------- Problem 3: TreeNode * makeComplete(int level, TreeNode * parent) { if (level == 1){ return new TreeNode("",0,0,parent); } TreeNode * root = new TreeNode("",0,0,parent); root->left = makeComplete(level-1,root); root->right = makeComplete(level-1,root); return root; } +1 handle base case, identify it, return new node +2 create node and pass it as parent for recursive call must have both -1 if all done in one line, e.g., TreeNode * root = new TreeNode("",makeComplete(level-1,root), makeComplete(level-1,root),parent); since order of ops on root having valid value is questionable +1 for level-1 in recursive calls +2 for using return values of recurive calls in creating node and for returning value -1 for missing return value -2 if return values ignored thus this appears as 3 points, but only counts max 2 --- Part B: T(n) = 2T(n-1) + O(1) which is O(2^n) -3 most people wrote: 2T(n/2)+O(1), this was to get partial credit without recurrence was possible, but answer should be 2^n Part C: void assign2leaves(TreeNode * root, tstack& names) { if (root->left == 0 && root->right == 0) {// isleaf names.pop(root->info); } else { assign2leaves(root->left,names); assign2leaves(root->right,names); } } +2 identify leaf as base case (ok to call isLeaf unwritten) +2 pop/assign value to root node +1 try +1 right +2 two recursive calls properly done only on non-leaves Part D: void assignWinners(TreeNode * root, tmap, string> * winnerMap) { if (root->left != 0){ //not leaf assignWinners(root->left,winnerMap); assignWinners(root->left,winnerMap); root->info = map->get(make_pair(root->left->info,root->right->info)); } } +1 identify non-leaf as recursion/non-base case +2 make recursive calls syntactically right and before assigning values if calls made in wrong order, then -2 +2 assign value to info +1 try +1 syntax right with make_pair and map->get ---- Part A: Code as is is O(n log n): why? visits every node once and does O(log n) work for every node since contains and insert are both O(log n) If insert call is moved then it's O(n^2) because nodes will be inserted in order and thus bad tree --> yields 1 + 2 + ... + n Recurrences don't work here, most students used them. If student uses recurrences then -3 unless there's a mitigating explanation. They can't get more than +1 unless they mention the order in which the resulting tree is created and how that affects code Part B: Worst case is O(n^2), again, recurrences can't justify properly, but most people will get the answer correct here. +3 for answer and +1 for justification: -1 for just using recurrence Part C: There are 3 parts: 1. tree2vector: 4 points 2. merge vectors: 4 points 3. vector2tree 4 points and call them right from xor 1. Most students lost points in tree2vector because they didn't pass the vector in, used a local one which was returned -2 vector not right because it's local and has only one value 2. merge -2 if don't treat equal values right, many students just hand one if, and let the equal elements be merged, but they need to be omitted 3. Some students created a long stringy, ugly tree in O(n) time, but I took off -1 for an ugly tree even though this meets the specs Otherwise I took off -1 in each of 1,2,3 for minor errors and gave no credit for crap. void tree2vector(TreeNode * t, tvector& v) { if (t != 0){ tree2vector(t->left,v); v.push_back(t->info); tree2vector(t->right,v); } } TreeNode * vector2tree(const tvector& v, int left, int right) { if (left <= right){ int mid = (left+right)/2; TreeNode* root = new TreeNode(v[mid], vector2tree(v,left,mid-1), vector2tree(v,mid+1,right)); } return 0; } void xor(TreeNode * a, TreeNode * b){ tvector av,bv,result; tree2vector(a,av); tree2vector(b,bv); int ai = 0, bi = 0; while (ai < av.size() && bi < bv.size()){ if (av[ai] < bv[bi]){ result.push_back(av[ai]); ai++; } if (bv[bi] < av[ai]){ result.push_back(bv[bi]); bi++; } else { ai++; bi++; } } while (ai < av.size()){ result.push_back(av[ai++]); } while (bi < bv.size()){ result.push_back(bv[bi++]); } return vector2tree(result, 0, result.size()-1); }