NAME: EE 322C Spring 2006 (Chase) Final READ THIS FIRST. Please use the back side of each page for scratch paper. I will not give credit for illegible answers (and I reserve sole judgment for what is “legible”). The exam is out of 100 points, not all questions are equally hard or take equally long to complete. Good luck! 1. (25 pts) A Bag is a data structure that stores a collection of objects. The collection is not really a set, because the Bag can have duplicates. The collection is not really a sequence, because there is no ordering defined for the Bag. Implement a template class Bag<T>. Please use inheritance to make the implementation as simple as possible. Your base class should be a vector<T>. In other words, your Bag should be built using a vector to provide the basic storage (no trees, hash tables, or linked lists please). Credit will be awarded for each of the following parts. READ THE ENTIRE QUESTION (at least skim it) BEFORE STARTING ANY OF THE PARTS!!! a. (4 pts) Give a template definition for Bag<T>. You must show the inheritance that you intend to use, and include all data members. Be sure to save some space, as you may think of some new data members once you’ve answered parts b through d. Please do not show any member functions in part a (we’re saving member functions for the subsequent parts). b. (4 pts) Write a function insert that takes an argument of type T and adds that to the collection. This function must run in amortized constant time. c. (5 pts) Write a member function find that takes an argument of type T and returns an iterator. If the argument exists inside the Bag then find returns an iterator pointing to any one of the positions where that object can be found (remember, a Bag can have duplicates). If the argument cannot be found, then find should return end(). This function must run in no worse than linear (O(N)) time. d. (5 pts) Write a member function called remove that takes an argument of type iterator. If the iterator points to a position other than end(), the object at that position is removed from the container. If the iterator points at end(), then remove does nothing. Note that once the object has been removed, the iterator will (obviously) no longer point to that object. After remove, the iterator should point to some successor object in the Bag (or to end, if there are no successor objects). To make this requirement more concrete, the following example must run in O(N) time for a Bag<int> with N elements and it must remove all the odd values from x. void removeOdds(Bag<int>& x) { Bag<int>::iterator p = x.begin(); while (p != x.end()) { if (*p % 2 == 0) { ++p; } // an even value, skip it else { x.remove(p); } // an odd value, delete it (don’t increment p) } } e. (4 pts) Our Bag requires several other member functions not listed above. For each of the following functions, indicate if the function would be inherited, trivial, or complex (i.e., this is a multiple choice problem, and those are your three choices). A function counts as “trivial” if either it has no statements (i.e., it’s just “{}”), or if it is not required because the compiler will generate an acceptable function for you. Any other function (even if it has just one line) counts as “complex”. Note that vector<T> has a member function defined for each item in this list. i. A no-argument constructor ii. A destructor iii. begin() iv. end() f. (3 pts) vector<T> provides an operator[] that really does not make sense for a Bag (bags are not sequences, and anything that suggests that the values are ordered would be misleading). I’d like to make sure that my Bag does not inherit []. What can I do about this? (note, I’m specifically excluding private inheritance, for two reasons – first, we agreed that private inheritance was not a topic I’d test you on, and second, I don’t think that’s the best way to do this at all) What should I do to prevent people from creating a Bag<T> named x and then calling x[42]? Please be as specific as possible, if there’s code that you can write to solve this problem, write the code! 2. (6 pts) When we talked about hash tables, we always assumed that the hash function could be computed in O(1) time. However, our hash function for strings was clearly linear in the length of the strings (i.e., Θ(s) where s is the number of letters in the string)). What is the actual worst casse time complexity for the find function in a hash table under the following assumptions? a. N elements in the hash table, the load factor is < 1 and the hash function takes O(s) time to run (your answer may depend on both N and s). b. Same assumptions as part a but in addition, the hash function provides simple uniform hashing. 3. (12 pts) Consider the remove function. Every data structure can have a remove function (of course, each data structure’s function will be different). Imagine a remove function that takes an iterator as an argument. The remove function should remove from the data structure the object that is pointed to by the iterator. The ordering properties (if any) of the data structure should be preserved – in other words, if we were to remove an object, and then iterate over the entire data, then we’d see the same sequence of values as we did before, except the removed value would be missing from the sequence. What would the worst case time complexity of remove be for each of the following data structures (please assume that remove is implemented as efficiently as possible given the limitations of the data structure). a. Red/Black tree b. Hash table c. Doubly-linked list d. Vector (also known as ”expandable array”) 4. (8 pts) Assume I have a template class of the form HashTable<typename K, typename H>, where type K is the type of data stored in the hash table (the key type) and H is a function object that will serve as a hash function. Assume that I wasn to instantiate this template with K equal to double. Write a function object that I can use for H. Half of the credit for this problem is awarded for creating any kind of reasonable function object, and half will be awarded for creating a good hash function. Of course, you can only write one answer, so try to write a function object that is a good hash function for type double. You may assume that double is a 64-bit type, and that int is a 32-bit type if that proves relevant to your answer. 5. (8 pts) Yes/No questions relating to inheritance a. Could it make sense for an abstract class to have data members? b. Could it make sense for an abstract class to have a constructor? c. Should static functions be virtual? d. If I have 6 int data members in my base class, and 2 int data members in my derived class, should I expect that my Base class objects will take up three times as much memory as a derived class object? 6. (10 pts) Although we don’t normally think of integers as being data structures, they can act a bit like them. Each integer is actually the product of one or more prime numbers. Hence, an integer (like the number 60) is actually a data structure containing a sequence of primes (in the case of 60, the primes are 2, 2, 3, and 5 – note that the prime number 2 is stored in 60 twice). Write a class PrimeBox and an iterator. A PrimeBox is a data structure that is created from an integer, and contains the sequence of primes for that integer. The iterator “points” at each of the primes inside the prime box.. For full credit, your solution must have O(1) space for both the PrimeBox object and the iterator object. Here’s an example of how a PrimeBox and its iterator might be used. int main(void) { PrimeBox x(60); PrimeBox::iterator p = x.begin(); while (p != x.end()) { cout << *p; ++p; } } 7. (4 pts) Consider the following. On the left, I have an object-oriented solution, and on the right I have a traditional solution. Review the two examples and answer the following questions. Please recognize that I can only give you credit if you explain your answer. a. (2 pts) Which version (the “normal” or the “object-oriented”) will use less memory? Assume that there will be millions of Foo and Bar objects created when the application is run. b. (2 pts) Which version (the “normal” or the object-oriented”) of “fun” will run faster?. class Base { public virtual void doit(void) = 0; }; class Foo : public Base { public: virtual void doit(void) { cout << “Foo!”; } }; class Bar : public Base { public: virtual void doit(void) { cout << “Bar.”; } }; void fun(Base* b) { b->doit(); } class Base { public int type; }; class Foo : public Base { public: Foo(void) { type = 1; } }; class Bar : public Base { public: Bar(void) { type = 2; } }; void FooDoit(Foo* p) { cout << “Foo!”; } void BarDoit(Foo* p) { cout << “Bar.”; } void fun(Base* b) { switch (b->type) { case 1: FooDoit((Foo*) b); break; case 2: BarDoit((Bar*) b); break; } 8. (15 pts) Design a class that supports “saturating arithmetic”. With saturating arithmetic, overflow is handled by setting the result equal to the maximum value. So, if we establish that 100 is the maximum value and we want to add x and y, then if the sum is greater than 100, we return 100 as the result (i.e., all arithmetic “saturates” at the defined maximum). Here’s what I’d like you to do. I’m going to divide the work into parts to make it easier to assign partial credit for this problem (5 pts for each part). a. Define a class SatInt including a constructor and whatever operator(s) you need to make the following program work. The following program should print 42. int main(void) { SatInt x(42); cout << x << endl; } b. Write a function so that the class maximum value can be set to 100 using the following statement. Note that I would prefer that you did not use any global variables in your solution, although you can use static data members or ordinary (non-static) data members as you see fit. This program should print 100. int main(void) { SatInt::setMax(100); SatInt x(412); cout << x << endl; } c. Write the code that allows me to perform addition on two SatInts. The following program should print 42 and then 100 int main(void) { SatInt::setMax(100); SatInt x(21); cout << x + x << endl; cout << x + x + x << endl; } Show your complete solution below. Each of the parts above is worth 5 points. 9. (4 pts) Consider the following class, and then indicate for each of the following if the function call is legal class Cake { }; class ChocolateCake : public Cake; void eat(Cake x) { cout << “burp”; } void love(ChocolateCake y) { cout << “yummy!”; } Cake cake; ChocoateCake choc; a. love(cake) b. eat(choc) 10. (8 pts) Consider the following class and indicate the result of each of the following expressions class B { public: virtual void vf(void) { cout << “B::vf”; } void f(void) { cout << “B::f”; } }; class D : public B { public: virtual void vf(void) { cout << “D::vf”; } void f(void) { cout << “D::f”; } } int main(void) B bob; D dob; B* ptr = &dob; B& roob = dob; } a. bob.vf(); b. ptr->f(); c. ptr->vf(); d. roob.vf();