CS140 Final

Spring 2018

Instructions

  1. Use the Code Assessor development website, cs102dev.eecs.utk.edu, to answer each of the coding questions.
  2. Leave this worksheet next to your computer for the next lab section.
  3. You may have a one page cheat sheet, with writing on front and back.
  4. The name of the coding question on Code Assessor is shown next to each question.
  5. The total points available for this exam (the lab coding questions plus the paper exam) is 150 points with 60 points being for the coding portion and 90 points for the paper portion.
  6. You may not be able to finish all the problems. If so, please do not worry. We will examine all answers (even correct ones) and award partial credit.
  7. We will look at coding style and efficiency in grading your problems. Even if your answer passes the test cases, you might lose points if your solution is overly inefficient, is hard to read, uses poor variable names, etc.
  8. For both exam problems you must use recursion to solve the problem. If you fail to use recursion then you will receive a 0 for the problem.
  9. You have the entire lab period. You may leave when you are finished. Please do not discuss the exam with anyone from another lab section until after 2:30pm.
  10. Good luck!

  1. (30 points--CS140Sp18-Final-Recursion): You may recall binary search from COSC 102. If not, here is a quick review. Binary search is performed on a sorted vector. You compare the search key with the middle element of the vector. If the search key matches this element, you return the value associated with that element of the vector. If the search key is less than this element, then you search the lower half of the vector. If the search key is greater than this element, then you search the top half of the vector. You use two indices, low and high, to denote the lower and upper bounds of the vector that you are searching. You compute the middle element as (low + high)/2. The stopping conditions are: 1) the middle element matches the search key, or 2) low becomes greater than high, in which case the search key is not in the vector.

    Write a recursive function named binsearch that implements the above algorithm. Your function header is:

    Person *binsearch(string key, vector<Person *> &students, int low, int high);
          
    The four parameters are defined as follows:

    1. string key: The search key, which is the name of a student.
    2. vector<Person *> &students: The vector of students that you are searching. Notice that students is passed as a reference parameter.
    3. int low: The lower bound of the portion of the vector you are searching.
    4. int high: The upper bound of the portion of the vector you are searching.
    low and high are inclusive, meaning that the elements to which they point are part of the vector being searched. For example, if low is 3 and high is 8, then the elements at indices 3 and 8 are part of the vector being searched.

    Your binsearch procedure returns a pointer to the student object containing the search key or NULL if the search key is not in the vector. A student object is declared as follows:

    class Person {
       public:
          string name;
          double gpa;
          string hometown;
    };
    
    You will be comparing the search key against the name field of student objects in the vector.

    Constraints:

    1. The vector is guaranteed to be in sorted, alphabetical order based on student name.
    2. The vector is guaranteed to contain at least one element
    3. For the initial call to binsearch, low is guaranteed to be less than or equal to high (they could be equal if the vector has size 1).
    4. Don't read or print anything in your binsearch procedure. The test driver that has been provided does all necessary reading and writing.

    Test Input: Here is some sample input. We have written a test driver that reads this input, creates the vector of Person objects, and then calls your binsearch procedure with search keys:

    smiley 3.6 Knoxville
    nels 4.0 San_Jose
    josie 4.0 Nashville
    tom 2.75 Chattanooga
      
  2. (30 points--CS140-Sp18-Final-ExpressionTree):

    A binary expression tree is one in which the interior nodes are operators, such as + or -, and the leaves are numbers. For example:

                         +
    		  /     \
                     -       +
    	       /   \    / \
                  -     4  8  10
                 / \
    	    16  5
         
    This expression tree represents the arithmetic expression:
         ((16-5) - 4) + (8+10)
         
    which evaluates to 25. An expression tree can be evaluated by evaluating the left subtree, then the right subtree, and finally performing the indicated operation on the values returned by evaluating the left and right subtrees. A leaf evaluates to its number. For example, in the above expression tree, the + node with the leaves 8 and 10 evaluates to 18 since its left subtree evaluates to 8 and its right subtree evaluates to 10. Similarly the root + node evaluates to 25 because its left subtree evaluates to 7 and its right subtree to 18.

    You are given the following class declaration for an expression tree node:

    class ExpNode {
    protected:
      double value;
      char operation;
      bool leaf;
      ExpNode *left;
      ExpNode *right;
    public:
      ExpNode(double v) : value(v), leaf(true) {}
      ExpNode(char opn, ExpNode *leftChild, ExpNode *rightChild) 
        : operation(opn), left(leftChild), right(rightChild), leaf(false) {}
      double evaluate();
    };
    
    Your task is to write the method named evaluate. It should evaluate the subtree rooted at that ExpNode and return the value of computed expression as a double. If the node is a leaf, then the leaf variable will be true and the value field will be set to a number. If the node is an operation node, then leaf will be false and the operation field will contain the node's operator, which may be one of '+', '-', '*', and '/'.

    If the node is a leaf, evaluate will return the number associated with the leaf. Otherwise evaluatewill evaluate the left and right subtrees, perform the operation indicated by the operation field on the results returned by the left and right subtrees, and return this value.

    We have provided a testdriver in main that reads in a prefix arithmetic expression, constructs an expression tree from it, and calls the root node's evaluate method. Some sample inputs would be:

    5                // evaluate should return 5
    * 3 4.2          // evaluate should return 12.6
    - 4 9            // evaluate should return -5
    * + 3 6 4        // evaluate should return 36. The input represents the
                     //   expression (3 + 6) * 4
    / * 8 5 - 4 2    // evaluate should return 20. The input represents the
                     //   expression (8 * 5) / (4 - 2)