- (14 points) For each of the following questions circle the best answer from the
above list.
Sometimes it may seem as though two or more choices would
be equally good. In those cases think about the operations that the
data structures support and what I said about them in class. Then
choose the data structure whose operations
are best suited for the problem.
You may have to use the same answer for more than one
question.
a. vector
b. list
c. deque
d. map
e. set
f. multimap
g. multiset
h. hash table
- a b c d e f g h : The data structure which most efficiently allows you to determine whether a voter has already voted. It should be possible to insert voters into the data structure and quickly find voters. The voters do not have to be kept in sorted order. You may assume that voters have unique voter ids.
- a b c d e f g h You are working for a somewhat less than reputable airline that multiply books its seats. You need to store seat/name pairs into a data structure where seat is the seat number and name is the name of the person sitting in the seat. seat is the key and name is the value. Since our airline multiply books its seats, a seat may appear in your data structure multiple times. Your data structure should efficiently handle inserts, deletes, and finds. It should also keep the seats in sorted order.
- a b c d e f g h You are simulating a system that has 10 elevators. Your data structure will store the records for each of the elevators and the elevators are numbered from 0-9. You need to be able to efficiently add the records for the 10 elevators to the data structure and find the records for an elevator, given the elevator's number. No deletions are required.
- a b c d e f g h The data structure you should use
if you wanted to model a line at a bank, where arriving customers are put at the end of the line and customers are served from the front of the line.
- a b c d e f g h The data structure you would use to keep track of email messages with date/message pairs. The date is the key and the message is the value. It is important that you be able to efficiently retrieve all messages in a certain date range, so the dates should be stored in order. The dates might not be unique (i.e, you might have several messages on the same day).
- a b c d e f g h Graphical interfaces often draw objects on top of each other. To keep track of the drawing order of objects, they need to maintain a data structure where the first object is the bottommost object, the second object is the second bottommost object, and so on to the last object, which is the top object on the display. For example, suppose you had four objects--a circle, a square, a triangle, and a text label--and they need to be drawn in that order. The data structure would keep pointers to each of these four objects in that order. The operations required are 1) efficient addition to the front or back of the objects, 2) efficient deletion in the middle of the objects (this may require a linear traversal to find the object), and 3) efficient traversal through the collection of objects.
- a b c d e f g h You want to keep track of the number of times that each word has appeared in an essay. You need a data structure that allows you to efficiently insert new words and update the count associated with each word. You need to keep the words in alphabetical order.
- (11 points) Suppose I have the following declarations and code:
int a, b, c;
int *p, *q, *r;
Also suppose that the above variables are assigned the following memory addresses:
a: 0x1000
b: 0x1004
c: 0x1008
p: 0x100c
q: 0x1010
r: 0x1014
What values are printed by the following two cout statements. Note that
the first cout statement prints p and q whereas the second cout statement
prints *p and *q.
p = &c;
*p = 20;
p = &a;
*p = 40;
q = p;
b = *q + *p;
cout << a << " " << b << " " << c << " " << p << " " << q << endl;
a = 60;
b = 20;
c = 70;
r = &b;
b = a + c;
p = new int;
*p = 2;
q = new int;
*q = a / *p;
cout << a << " " << b << " " << c << " " << *p << " " << *q << " " << *r << endl;
cout 1: cout2:
a: a:
b: b:
c: c:
p: *p:
q: *q:
*r:
- (6 points) For each of the following definitions, select the
term from the following list that best fits that definition.
a. memory leak b. dangling pointer c. seg fault
- __________________ Occurs when you try to de-reference a NULL
pointer (e.g., p = NULL; *p = 20).
- __________________ Occurs when you forget to delete a heap-allocated
object when there are no longer any pointers to that object (i.e., you can no longer reference that object).
- ___________________ Occurs when you pre-maturely de-allocate a
heap-allocated object when there are still pointers to that object (i.e., when parts of the program may still try to reference that object).
- (10 points) Explain what is inefficient about the
following code segment and how you would fix it. The code segment is
supposed to use linear probing to find a key in a hash table. It should
return the index of the key if it is found in the hash table and -1
if the key is not in the hash table. The keys
are guaranteed to be non-negative integers. A hash table entry stores
one of three values: 1) a key, 2) a value of -1 to denote that the entry
has never stored a key, and 3) a value of 0 to denote that the entry is
currently empty but once stored a key. A sample hash table might be:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
-1 | 12 | 23 | 0 | 48 | 35 | -1 | -1 | 30 | -1 | 32 |
int find(vector<int> &hashTable, int key) {
unsigned int bucket = key % hashTable.size();
int i;
1 for (i = bucket; i != bucket-1; i = (i+1) % hashTable.size()) {
2 if (hashTable[i] == key) {
3 return i;
4 }
5 }
6 return -1;
}
- In four sentences or less, describe why the above find function is
inefficient (i.e., although this code returns the correct result, it does
not faithfully implement the find algorithm for linear probing).
- Fix the inefficiency by writing an appropriate code segment and
indicate where you would insert it (i.e., after which line number).
- (9 points) Your boss has asked you to choose between two algorithms. The
two algorithms have the following running times:
Algorithm 1: T(n) = 1000n*log2n + 20n + 3
Algorithm 2: T(n) = 0.5n2 + 50n + 8
- What is the Big-O running time of Algorithm 1?
- What is the Big-O running time of Algorithm 2?
- The size of the input will be typically quite large, on the order
of n > 1000. Which of the two algorithms should you choose and why?
- (10 points) You are told that the running time of an O(n) algorithm for
a certain value of n is 8 seconds.
- How long in seconds would you expect an O(log2 n) algorithm to take for
the same value of n? If you forget how to compute log2n, it is the value of
x, where 2x = n.
- How long in seconds would you expect an O(n2) algorithm to
take for the same value of n?
- (10 points) In the Coke lab, one of your functions had to delete a user. One of
the tasks this function had to perform was to remove all of
the user's phones from the Phone map. To refresh your memory,
here are the relevant data structures:
class User {
public:
string username;
string realname;
int points;
set <string> phone_numbers;
};
map <string, User *> Phones;
Here are two code segments that correctly remove all of
the user's phones from the Phones map:
(a) | (b) |
void deletePhones(User *u) {
set::iterator phone_it;
for (phone_it = u->phone_numbers.begin();
phone_it != u->phone_numbers.end();
phone_it++) {
Phones.erase(Phones.find(*phone_it));
}
}
|
void deletePhones(User *u) {
map<string, User*>::iterator phone_it;
// phone_it is correctly advanced in the loop body, which is
// why there is no phone_it++ as the third expression in the for header
for (phone_it = Phones.begin();
phone_it != Phones.end(); ) {
if (phone_it->second == u) {
phone_it = Phones.erase(phone_it);
}
else {
phone_it++;
}
}
}
|
Make the following assumptions:
- There are n phones in the Phones map
- n is a very large number (e.g., > 50,000,000).
- The number of phones that belong to any user is capped by a small
constant c (c < 5).
Answer the following questions:
- Which code segment is more efficient (a or b)?
- Explain why the code segment you selected is more efficient using
Big-O notation. You will need to compute the Big-O running time of
each of the two functions in order to justify your answer.
- (10 points) Draw a list diagram that shows what the following list looks
like pictorially after the following code executes.
Make sure that you include sentinel
nodes and that you show where each list iterator points. Your diagram
should look like the ones in the class notes.
list<int> names;
list<int>::iterator nit, nit1;
names.push_back(45);
names.push_back(100);
names.push_front(80);
names.push_front(50);
names.push_front(200);
nit = names.begin();
nit++;
nit++;
nit1 = nit;
nit1++;
names.erase(nit);
nit = nit1;
nit++;
names.insert(nit, 15);
nit1 = names.end();
- (10 points) Assume you have the following declaration:
class ListNode {
public:
string name;
ListNode *next;
ListNode *prev;
ListNode(string n) : name(n) {}
};
Further suppose that a series of inserts have created the following list:
- Draw the diagram that results when the following code is executed:
ListNode *newnode;
1) newnode = new ListNode("Brad");
2) newnode->next = currentNode;
3) newnode->prev = currentNode->prev;
4) currentNode->prev = newnode;
5) currentNode->prev->next = newnode;
Warning: There is a flaw in this code and so you should not automatically
draw the diagram you think will appear. In fact, you should draw the
nodes in the region of the new node on a separate piece of paper and only
when you are sure of the solution, copy your solution to the exam. Otherwise,
if you are using ink, you will find that you have to scratch out your answer
and redo it.
- How could you modify this code so that is correctly inserts newnode
into the list? Be specific by identifying a) which line(s) of code are
causing the problem, and b) how you would modify these lines (write
the modification as C++ code).