Test 2 Practice Problems, CPS100e, Fall 1996


Thinking about Thinking about Sorting

  1. Both bubble sort and seelction sort are O(n^2) sorts. Why is the running time of bubble sort so much slower than selection sort, and why is this difference more pronounced when strings are sorted (as compared to, for example, integers)
    
    
    
    
    
  2. True/False: There are no sorts with running times faster than O(n log n).

  3. Give one reason to prefer mergesort to quicksort and also one reason to prefer quicksort to mergesort.
    
    
    
    
    
  4. The function DoMerge from sortall.cc is modified (as shown below) to divide a vector into three parts, recursively sort each of the three parts, then merge the parts back together. One line is missing from the function DoMerge, add the line so that the function works properly.

    Write a recurrence relation for the version of mergesort described above and implemented in DoMerge. What is the solution of this recurrence relation, i.e., the big-Oh complexity of the new version of mergesort? You do NOT need to solve the recurrence, a one sentence justification is sufficient (although you may solve the recurrence).

    template <class Type> void Merge(Vector<Type> & a, int left,int mid,int right) // precondition: a sorted from a[left] ... a[mid] and from // a[mid+1] to a[right] // postcondition: a sorted from a[left] ... a[right] template <class Type> void DoMerge(Vector<Type> & a, int left,int right) // postcondition: a[left] <= ... <= a[right] { int onethird = (right - left)/3; // [left..first] is first interval int first = left + onethird; // [first..second] is second interval int second = right - onethird; // [second..right] is third interval if (left < right) { DoMerge(a,left,first); DoMerge(a,first+1,second); DoMerge(a,second+1,right); Merge(a,left,first,second); // one call of Merge missing below } }

  5. Suppose a struct Student for students in a class is (partially) declared as below. Each student has a name, a list of grades, the number of grades stored in the list, and a class identification number. In a class of N students, each student is given an ID number in the range 1 ... N, e.g., in a class of 20 students the numbers 1 ... 20 are assigned one per student.

    struct Student { int classID; // ID number string name; // name of student "Jane Doe" Vector<int> grades; // list of grades int numGrades; // # of items stored in grades };

    Write the function SortStudents whose header is given below. SortStudents sorts the information in list so that it is in order from smallest (1) to largest (N) student ID number.

    Your solution MUST sort in O(N) time!!

    hint: in what slot does the student with ID number 16 belong?

    void SortStudents(Vector<Student> & 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)

Stacks and Lists and Queues (oh my)

  1. Write the function MergeStacks whose header is given below. MergeStacks creates a new stack, result. The top of result should be the top of first, followed by the top of {\em second}, and then alternating between first and second. If second empties before all items are popped from both stacks, the remaining items are popped from first.

    For example, stacks of ints are shown below with the result of merging first and second.

    *

    Because stacks first and second are passed as const reference parameters, local copies must be made that can be popped.

    void MergeStacks(const Stack<int> & first, const Stack<int> & second, Stack<int> & result) // precondition: # elements in first >= # elements in second, result is empty // postcondition: result = merge of first and second (starting with first)

  2. For a Node declaration, see below.

    Write a function JoinLists that creates a new linked list formed by first copying all the values in list a followed by all the values in list b. Lists a and b should be unchanged --- JoinLists creates a new list. JoinLists returns a pointer to the first node of the newly created list.

    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

Tree Problems


Assume the following declarations have been made struct Tree { int info; Tree * left,* right; Tree(int val, Tree * lbranch = 0, Tree * rbranch = 0); }; Tree::Tree(int val, Tree * lbranch, Tree * rbranch) : info(val), left(lbranch), right(rbranch) {} The function Heightreturns the height of a tree. int Height(Tree * t) // postcondition: returns height of tree with root t { if (t == 0) { return 0; } else { return 1 + Max(Height(t->left),Height(t->right)); } } where the function Max returns the largest of its two integer parameters.

The function IsLeaf returns true iff its parameter is a leaf.

bool IsLeaf(Tree * t) // postcondition: returns true if and only if t is a leaf { if (t != 0 && (t->left == 0 && t->right == 0)) return true; return false; }
  1. Write a function NonLeafCount that has a single parameter of typeTree * and that returns the number of non-leaf nodes in the tree whose root is pointed to by the parameter.

  2. Describe an O(N) algorithm for determining when two binary search trees s and t contain exactly the same values, i.e., all values in s are in t and all values in t are in s (here N is the number of nodes in the largest tree.)

    (You Don't have to write code, describe a method that works)

    bool HaveSameValues(Tree * s, Tree * t)
  3. Write a function Tree2List that converts a binary search tree into a sorted linked list. You may find it useful to write auxiliary function(s) called by Tree2List. struct Node { int info; Node * next; Node(int val, Node * ptr = 0) : info(val), next(ptr) {} }; Node * Tree2List(Tree * t) // precondition: t is the root of a binary search tree // postcondition: returns a pointer to the first node of a // sorted linked list containing same values as t

  4. Write the function HasPathSum whose header is given below. HasPathSum returns 1 if there exists some root-to-leaf path in the tree whose root is t whose nodes sum to target and returns 0 otherwise.

    For example, in the tree shown below there are exactly four root-to-leaf paths. The sums of the paths are 27, 22, 26, and 18. Thus the value of HasPathSum(t,27) should be true and the value of HasPathSum(t,30) should be false (assuming t points to the root of the tree --- the node whose info field has value 5.)

    *

  5. The tree below is a binary search tree.

    *

    Write the function DoPrintPath that prints, for each leaf in tree, one line of output consisting of the values stored on the root-to-leaf path. For example, for the tree above the output is shown below.

        lemon cherry apple
        lemon cherry guava
        lemon papaya orange
        lemon papaya tangerine watermelon
    

    The function DoPrintPath is called by PrintPath as shown below. You may assume a boolean-valued function IsLeaf exists. Assume that there at most 200 strings stored in any tree. (Note that the Tree struct is now templated).

    void DoPrintPath(Tree<string> * tree, Vector<string> & 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 { // WRITE THIS FUNCTION! } void PrintPath(Tree<string> * tree) { if (tree != 0) { Vector<string> path(200); path[0] = tree->info; DoPrintPath(tree,path,0); } }