Answers to test 2 practice problems Thinking about Thinking about sorting 1. Bubble sort is slower because it is swap intensive: O(n^2) swaps vs. O(n) swaps for selection sort. Strings are slower because they use more memory so take longer to swap. 2. Radix sort is O(n) for n numbers assuming a fixed maximum number of digits. If the size of the numbers is unconstrained, then the complexity becomes O(n log MAX) where MAX is the largest number. The (log MAX) is the number of digits in MAX. 3. Mergesort has better worst case performance than quicksort: O(n log n) vs O(n^2) for quick. Quicksort is usually faster in general. This is true for vectors, mergesort is often faster with linked lists. 4. The missing call is Merge(a,left,second,right); The recurrence is T(n) = 3*T(n/3) + O(n), the solution is O(n log n) 5. void SortStudents(Vector & list, int numElts) // precondition: numElts = # of students in list // ID numbers are unique and in range 1...numElts // postcondition: list is sorted into increasing order by // student ID number // performance: O(n) { int k; Student temp; Student current = list[0]; for(k=0; k < numElts; k++) { temp = list[current.classID - 1]; // student #1 in index 0, etc. list[current.classID - 1] = current; current = temp; } } // using aux vector void SortStudents(Vector & list, int numElts) // precondition: numElts = # of students in list // ID numbers are unique and in range 1...numElts // postcondition: list is sorted into increasing order by // student ID number // performance: O(n) { int k; Vector temp(numElts); for(k=0; k < numElts; k++) { temp[list[k].classID - 1] = list[k]; } list = temp; } MergeStacks 1. void MergeStacks(const Stack & first, const Stack & second, Stack & result) // precondition: # elements in first >= # elements in second, result // is empty // postcondition: result = merge of first and second (starting with first) { Stack copy1(first); Stack copy2(second); result.MakeEmpty(); // be sure! while (! copy2.IsEmpty()) { result.Push(copy1.Top()); result.Push(copy2.Top()); copy1.Pop(); copy2.Pop(); } // finish copy1 if necessary while (! copy1.IsEmpty()) { result.Push(copy1.Top()); copy1.Pop(); } // result is backwards, pop off into copy1 then copy back to result while (! result.IsEmpty()) { copy1.Push(result.Top()); result.Pop(); } result = copy1; } 2. Node * JoinLists(Node * a, Node * b) // postcondition: returns a pointer to the first node of a list // containing copies of all values in a followed by // all values in b { Node temp; // header node Node * last = &temp; // points to last node of list being built while (a != 0) { last->next = new Node(a->info); a = a->next; last = last->next; } while (b != 0) { last->next = new(b->info); b = b->next; last = last->next; } return temp->next; // points to first constructed node } TREE stuff 1. int NonLeafCount(Tree * t) { if (t == 0 || IsLeaf(t)) return 0; else return 1 + NonLeafCount(t->left) + NonLeafCount(t->right); } 2. Do an Inorder traversal of both trees s and t, storing values in a stack during the traversal (or a vector). Then check the stacks for equality by popping values. The traversal is O(n), the checking is O(n), the total is O(n). 3. Solution below is O(n log n) Node * Tree2List(Tree * t) // precondition: t is the root of a binary search tree // postcondition: returns a pointer to the first node of a { if (t == 0) { return 0; } else { // the left subtree Node * leftList = Tree2List(t->left); // the root and right subtree Node * root = new Node(t->info, Tree2List(t->right)); if (leftList == 0) // nothing there { return root; } else // something in the left subtree { Node * temp = leftList; while (temp->next != 0) { temp = temp->next; } temp->next = root; return leftList; } } } // the solution below is O(n) Node * Tree2List(Tree * t) // precondition: t is binary search tree // postcondition: returns pointer to first node of linked list // whose node-values are sorted (i.e., same order // as inorder traversal of t { Node * list = 0; DoList(t,list); return list; } void DoList(Tree * t, Node * & list) // precondition: list points to sorted linked list // postcondition: list points to first node of sorted linked list // values same as inorder traversal of t (coming // before any nodes already in list) // note: O(n) { if (t != 0) { DoList(t->right,list); // turn right subtree to list list = new Node(t->info,list); // root with right list next DoList(t->left,list); // process left subtree } } 4. int HasPathSum(Tree * t, int target) // postcondition: returns 1 if there exists root-to-leaf path in t // whose nodes sum to target, else returns 0 { if (t != 0){ if (t->left == NULL && t->right == NULL) // it's a leaf { return t->info == target; } else { return HasPathSum(t->left,target - t->info) || HasPathSum(t->right,target - t->info); } } return 0; } 5. void DoPrintPath(Tree * tree, Vector & path, int depth) // precondition: depth = depth of tree (# edges from root, root has // depth 0) // path stores values on root-to-leaf path // postcondition: for each leaf of tree, print values on root-to-leaf path // one line of output for each leaf { if (t != 0) { if (IsLeaf(t)) // at a leaf, print a path { int k; for(k=0; k < depth; k++) { cout << path[k] << " "; } cout << tree->info << endl; } else { path[depth] = tree->info; DoPrintPath(tree->left,path,depth+1); DoPrintPath(tree->right,path,depth+1); } } } void PrintPath(Tree * tree) { if (tree != 0) { Vector path(200); DoPrintPath(tree,path,0); } }