Data Structures — CSci 1200 Test 3 — Overview and Practice

Overview • Test 3 will be held Monday April 18, 2010, 12:00-1:30pm, Darrin 308. No make-ups will be given except for emergency situations, and even then a written excuse from the Dean of Students office will be required.

• Main coverage will be from Lectures 11-18, Labs 6-10, and HW 5&6, but there will be a question pertaining to our implementation of the vector class from Lecture 8 and Lab 5.

• The test will be closed-book and closed-notes. We will provide a separate handout at the start of the test summarizing some of the syntax from the including std::vector, std::string, std::list, std::map, std::.

• Solutions to many of these questions will be provided by Thursday April 14.

• How to study?

– Work through the sample questions, writing out solutions! You are encouraged to form teams to do this with other students. – Review and re-do lecture exercises, lab and homework problems. – Identify the problems that cause you difficulty and review lecture notes and background reading on these topic areas.

Summary of the Primary Functionality of Maps A summary like this will appear as part of the test handout:

• A particular instance of a map is defined as

map< key_type, value_type > var_name

• Maps store pairs of “associated” values.

pair< const key_type, value_type >

• Map iterators refer to pairs.

• Map operator[] is a function call taking a key_type as an argument and returning a reference to the associated value_type in the map.

• The find member function of maps is

m.find( key );

where m is the map object and key is the search key (of type key_type). It returns a map iterator, which is the end iterator if no pair in the map has key as its first value.

• The map insert member function m.insert( make_pair( key, value ) );

returns a pair

pair< map::iterator, bool >

where second value is true if the pair was inserted and false if a pair having the key was already in the map.

• Maps provide three different versions of the erase member function:

– void erase( iterator p ) — erase the pair referred to by iterator p. – void erase( iterator first, iterator last ) — erase all pairs from the map start- ing at first and going up to, but not including, last. – size_type erase( const key_type& k ) — erase the pair containing key k, returning either 0 or 1, depending on whether or not the key was in a pair in the map

Questions 1. What is the worst-case time (using order notation) required by the std::map (and std::set), std::vector, and std::list container classes for each of the following operations? In all cases, you may assume that each comparison operation requires O(1) time.

(a) Find a particular value (key) in the container. (b) Find a particular value (key) in the container, assuming the container is already sorted. (c) Insert a value (key) into the container assuming the location to insert is already known. (d) Insert in the back of the container. (e) Erase a value (key) from the container, assuming the location is already known (f) Accessing the k-th value (key) in the container.

2. You are given a map that associates strings with lists of strings. The definition is

map > words;

Write a function that counts the number of key strings that are in their own associated list. For example, suppose the map contained just the following three key strings and lists

string list abc car, cab, jet, apple car horse, car, train, car jet buggy, abc

Then the function should return the value 1, since only car is in its own list. Start from the function prototype

int count( map > const& words )

3. You are given a map, assoc, that associates a string (the “key”) with a list of strings (the “value”). Its declaration is

2 std::map< string, list > assoc;

After processing input, assoc might contain, for example:

key value "apple" "banana", "cat", "tiger", "", "car", "forest" "banana" "car", "angel", "simple" "car" "apple", "angel", "forest" "forest" "rain", "tree", "monkey", "ape" "monkey" "tree", "banana" "tree" "apple", "forest" "zebra" "simple", "apple", "car", "horse"

Observe that "forest" is in the list associated with "apple", but that "apple" is not in the list associated with "forest". Write a function that takes assoc as a parameter and outputs to std::cout all pairs of strings, s1 and s2, such that s1 is in the association list of s2 and s2 is in the association list of s1. For the example above, the output should be

apple tree apple car forest tree

You may assume that the list associated with any string does not contain repeated strings, and you may assume all strings are lower case.

4. Write a function that takes a pointer to the head of a singly- and returns a pointer to the head of a new linked list that is a copy of the linked list, with the values in reverse order. You may not assume the existence of a reverse function. Here is the prototype

template Node* reverse_copy( Node* head )

5. Write a function to create a new singly-linked list that is a COPY of a sublist of an existing list. The prototype is

template Node* sublist( Node* head, int low, int high )

The new list will contain high-low+1 nodes, which are copies of the values in the nodes occupying positions low up through and including high of the list pointed to by head. The function should return the pointer to the first node in the new list. For example, in the following drawing the original list is shown on top and the new list created by the function when low==2 and high==4 is shown below.

3 Original list

head 3.1 2.4 8.7 9.4 14.2 0.9 0

New list

nhead 2.4 8.7 9.4

A pointer to the first node of this new list should be returned. (In the drawing this would be the value of nhead.) You may assume the original list contains at least low nodes. If it contains fewer than high nodes, then stop copying at the end of the original list. 6. Recall that the ds_list class is implemented as a doubly-linked list. Here is what you need from the class declaration, plus two additional function declarations.

template class ds_list { private: Node * head, * tail; int size; public: ds_list() : head(0), tail(0), size(0) {} void erase_all_x( const T& x ); void splice( int k, ds_list & other ); };

(a) Implement erase_all_x, which should remove all nodes from the ds_list that have the value of the parameter x. You may make no assumptions other than that ds_list is in a valid state (i.e. no incorrectly assigned pointers, etc.), and you may not use any member functions of the ds_list class. (b) Implement the splice member function. It should insert all nodes from other after the k-th entry of the ds_list and before the k+1-st. If the list is shorter than k then all values from other should be appended to the end of the list. It must do this without creating any new nodes. At the end of the function call the list other must be empty.

7. For this question and the next few, consider the following tree node class

template class TreeNode { public: TreeNode() : left(0), right(0) {} TreeNode(const T& init) : value(init), left(0), right(0) {} T value; TreeNode* left; TreeNode* right; };

4 Write a function to find the largest value stored in a of integers pointed to by TreeNode* root. Write both recursive and non-recursive versions. You may assume there is at least on node in the tree.

8. Write a function called Trim that removes all leaf nodes from a tree, but otherwise retains the structure of the tree. Hint: look carefully at the way the pointers are passed in the insert and erase functions discussed in lecture and lab.

9. Suppose you are given a vector that is known to contain many duplicate values. To illustrate, there may be 1,500 values, but only 75 of them are distinct. You could use std::sort to sort this vector, but a more efficent means is to use a std::map. Implement a sort function based on this idea. The prototype is

template void map_sort( vector & v )

For example, if the vector contains the following integers before the call to map_sort,

6, 10, 3, 7, 6, 11, 2, 3, 6, 10, 11, 6, 3

then after the call it should contain

2, 3, 3, 3, 6, 6, 6, 6, 7, 10, 10, 11, 11

10. Write a function that takes a pointer to the root of a binary search tree and two values, x0 and x1. The function should return the number of values stored in the binary search tree that are greater than or equal to x0 and less than or equal to x1. The function should be as efficient as possible, which means it should not visit all of the nodes and test their values unless it is absolutely necessary to do so (which would happen if all values in the tree were in the range [x0..x1]). Here is the function prototype:

template int count( TreeNode* root, T const& x0, T const& x1 )

You may assume type T has any necessary comparison operators defined on it.

11. Draw all valid three-node binary search trees containing the values 2, 5, 9.

5