CS140 Midterm 1 Solutions

Fall 2016

Instructions

  1. (12 points) For each of the following questions, select the best answer from the following list:

    linkercompilerlscdgdb
    g++vectorarraystructclass
    privateprotectedpublicsubstrfind

    1. ls The Unix command that lists the contents of a directory.

    2. protected The access protection you should use to declare the member variables (i.e., instance variables) of a class.

    3. array The data structure you should use to store a list of names when the number of names is known in advance.

    4. compiler The software that converts source code (i.e., .cpp files) to object code (i.e., .o files) and that outputs for each .o file a "function definition" table and an "unresolved function" table to aid subsequent software in combining .o files to form an executable.

    5. linker The software that combines two or more object files (.o files) into an executable and replaces references to functions with jumps to functions that are defined in other files.
    6. gdb The software tool you should use if you want to debug your C++ program.

  2. (10 points) Suppose I want to declare a class named Ballot and use it in a program named Vote.cpp. Answer the following questions:

    1. In what file should the class declaration for Ballot be placed? Give the name of the file, following the naming conventions discussed in class.

      Ballot.h

    2. In what file should the method definitions for Ballot be placed? Give the name of the file, following the naming conventions discussed in class.

      Ballot.cpp

    3. Using a single g++ statement, how could I compile all of these files into an executable named Vote?

      g++ -o Vote Vote.cpp Ballot.cpp

    4. Suppose instead that I wanted to separately compile these files and then link together their object files into an executable named Vote. Write the set of statements that would be required to perform this separate compilation and then linking into the executable.
        g++ -c Vote.cpp
        g++ -c Ballot.cpp
        g++ -o Vote Vote.o Ballot.o
      

    5. Suppose I want to execute Vote from the command line by 1) redirecting stdin from a file named ballots.txt, and 2) redirecting stdout to a file named results.txt. Write the command that I should use to execute Vote.

      ./Vote < ballots.txt > results.txt

  3. (18 points)

    1. Insert the keys 32, 49, 19, 52, 58, and 15 into the following two hash tables using a) separate chaining, and b) quadratic probing. For separate chaining, use commas to separate the keys that would go into an entry. For example, if the keys 32, 49, and 19 all went into the same entry, you would write (32, 49, 19) in that entry. You should use the hash function:
      h(key) = key % 13
      
       Separate ChainingQuadratic Probing
      05252
      1  
      21558
      3 15
      4  
      5  
      632, 19, 5832
      7 19
      8  
      9  
      104949
      11  
      12  

    2. For quadratic probing please also complete the following table showing the comma, separated list of entries you would probe in order to insert each key (e.g., if you examined entries 2, 3, and 7 in order to insert 32, then you would write (2, 3, 7) next to 32 in the below table--this is only an example--it is not the actual set of entries you will probe for 32). This will allow us to assign you partial credit if you do not get the entries correct in the quadratic probing hash table.

      keyquadratic probing
      326
      4910
      196, 7
      520
      586, 7, 10, 2
      152, 3

  4. (8 points) Suppose you are given the following code fragment:

    string name = "VJ";
    unsigned char h = 41;
    int i;
    for (i = 0; i < name.size(); i++) {
        h = (h << 2) ^ name[i];
    }
    	      

    where 'V' = 0x56 and 'J' = 0x4a What is the hexadecimal value of h after each iteration of the above loop? For this problem assume that unsigned ints are 8 bits

    iterationh
    0f2
    182
    You can trace through the code as follows:

    1. h = 41: 41 has the bit representation 0010 1001. You obtain this bit representation as follows:
            41 = (1 * 32 + 0 * 16) + (1 * 8 + 0 * 4 + 0 * 2 + 1 * 1)
               = (1 * 25 + 0 * 24) + (1 * 23 + 0 * 22 + 0 * 21 + 1 * 20)
      
    2. When i = 0, we are looking at name[0], which is 'V'. 'V' has the hexadecimal code 0x56 which is 0101 0110. So iteration 0 becomes:
      h = (h << 2) ^ 0101 0110      # I am separating the bits into groups of 4 for clarity
      
      1. h << 2: 0010 1001 becomes 1010 0100 because the left most two bits get shifted into the "bit bucket" and 2 0's get shifted into the right most two locations.
      2.   1010 0100 (h << 2)
        ^ 0101 0110 (0x56)
          ---------
          1111 0010 = 0xf2  (the answer to iteration 0)
             
    3. Starting with iteration 1, i is 1 and name[1] is 'J', which has the hexadecimal code '0x4a'. '0x4a' has the bit representation 0100 1010. So iteration 1 becomes:
      h = (h << 2) ^ 0100 1010
      
      1. h << 2: 1111 0010 becomes 1100 1000 because the left most two bits get shifted into the "bit bucket" and 2 0's get shifted into the right most two locations.
      2.   1100 1000 (h << 2)
        ^ 0100 1010 (4a)
          ---------
          1000 0010 = 0x82  (the answer to iteration 1)
        
    See the hashing and bit notes if you are having difficulty with bit operations.

    If you cannot figure out the hexadecimal value, you can give the base-10 value for a 2 point deduction.

      0xf2 = 242
      0x82 = 130
    
  5. (8 points) The following two code fragments are supposed to shift the numbers in a vector one element to the right and insert a new number at the front of the vector. For example, if the vector's contents are <3, 6, 10, 9> before the insertion, and the new number is 25, then the vector's contents should be <25, 3, 6, 10, 9> after the insertion. However, only one of the two code fragments performs this operation correctly.
    (a)(b)
    void insert(int num, vector<int> &data) {
         int i;
         data.resize(data.size()+1);
         for (i = 0; i < data.size()-1; i++)
             data[i+1] = data[i];
         data[0] = num;
    }
    void insert(int num, vector<int> &data) {
         int i;
         data.resize(data.size()+1);
         for (i = data.size()-2; i >= 0; i--)
             data[i+1] = data[i];
         data[0] = num;
    }
    Answer the following questions:

    1. (b) Which code fragment is correct (a or b)?
    2. Why is your selected code fragment correct?

      The code right-shifts each element of the vector by 1 position starting from the rear of the vector. By starting at the rear of the vector each element moves into a previously unoccupied "bucket" and does not clobber any other element of the vector. When the right shifting is complete, the first bucket in the vector is empty, and can be filled with the new number.

    3. Why is the other code fragment incorrect?

      The code tries to right-shift each element of the vector by 1 position, but it starts at the front of the vector. By starting at the front of the vector, each element moves into a previously occupied "bucket" and clobbers the element to its right. When the right shifting is done, every element of the vector is equal to the former first element of the vector and the original contents of the vector have been lost.

  6. (8 points) Both of the following functions read strings into a vector that can then be used by a calling function. Both code fragments are correct, but one is more efficient than the other.

    (a)(b)
    vector<string> readStrings() {
         string name;
         vector<string> names;
         while (cin >> name) {
             names.push_back(name);
         }     
         return names;     
    }
    void readStrings(vector<string> &names) {
         string name;
         while (cin >> name) {
             names.push_back(name);
         }
    }
    Answer the following questions:

    1. (b) Which code fragment is more efficient (a or b)?
    2. Why is your selected code fragment more efficient?

      It passes the vector to be filled by reference, rather than by value. Hence instead of copying the vector into the local parameter, the vector in main is used instead.

    3. Why is the other code fragment inefficient?

      It returns a vector from the function which requires that the returned vector be copied back to the main function. This copying is inefficient if the vector is large.

  7. (5 points) Write a typedef statement that declares a new type named Marbles to be a vector of strings.

    typedef vector<string> Marbles

  8. (6 points) Given the following variable declarations, write a printf statement that outputs hours, minutes, and seconds in the following format:
    	hh:mm:ss
          
    Each of the three fields should be 2 spaces wide. The minutes and seconds should be padded with a 0 if they are single digits, but hours should not be. For example "3:06:05".
    	int hours, minutes, seconds;
          

    printf("%2d:%02d:%02d", hours, minutes, seconds);

  9. (5 points) Write a line of code that converts a letter in a char variable named letter from its lower-case form, such as 'b', to its upper case form, in this case 'B' using character arithmetic. You will receive no points for using a function such as toupper. Assign the result back into letter

    letter = 'A' + letter - 'a';