CS140 Midterm 2 Answers
Fall 2009
- (20 points)
a. array
b. singly-linked list
c. doubly-linked list
d. linked-list queue
e. circular, array queue
f. stack
For each of the following questions choose the best answer from the
above list. Assume that the size of an array is fixed once
it is created, and that its size cannot be changed thereafter.
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 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:
- array The most space-efficient data structure you could use
to implement a
stack in which there is an upper limit, max,
on the number of elements that can be stored on the
stack.
- singly-linked list The most space-efficient data structure you could use
to implement a
stack in which the the number of elements that can be
stored on the stack is unlimited.
- array The data structure which allows any of its elements to
be accessed in constant time.
- doubly-linked list The data structure you would use if you needed to be
able to delete an element in the middle of the data
structure.
- stack The data structure you would use to ensure that the
last item added
to the data structure is always the first item removed
from the data structure.
- circular, array queue The most space-efficient data structure you could use
if you wanted to print out the last n lines of
a file where n is a command-line argument.
- stack The data structure you would use to check whether
a set of parentheses were balanced in an expression.
- linked-list queue The data structure you would use if you wanted to
simulate an unbounded
line of customers at a grocery store, where
customers are served from the front of the line and
customers enter at the back of the line.
- doubly-linked list The data structure that allows you to traverse its
elements in either forward or reverse order.
- circular, array queue The data structure that you could use to implement
streaming video on a video card with a fixed amount
of memory.
- (16 points) Suppose I want to write a library package for a vector. A vector is
an array that can be dynamically resized if it is not big enough.
The library would worry about calling realloc so the user does not
have to. I
want to use information hiding to implement the vector library. My
implementation will have two files named vector.h and
vector.c. Answer the following questions:
- I want to provide a function called new_vector that
is passed the initial vector size as an integer parameter. Write the
function declaration for new_vector (do not write the
definition, just the declaration as it would appear in the .h
file). I have intentionally
not told you what the return value is. You should be able to
figure it out based on the fact that I want you to use information
hiding.
void *new_vector(int size);
- void *: I want my vector to be able to store generic data. What should be
the type of the value that it stores. For example, if I have the
function description:
void vector_set(? vector, int index, ? value): Sets
the entry at the specified index to value.
what should be the declared type of value? That is
what should the ? before value be replaced by? (Please do
not tell me the type of vector--the question mark
before vector is there because I do not want to give away the
answer to question a).
- Where should the structs for the vector's implementation
be declared. Put a check mark next to the appropriate answer:
- __________ vector.h
- xxxxxxxxxx vector.c
- __________ the user's .c file
- Declare a struct for the vector's container
object. Use a typedef to name the struct "vector".
You need to figure out what fields the struct
needs and then declare them. Hint: The container
object will need to have a pointer to something that
holds the vector's data, it will need to keep track
of its capacity and it will need to be able to figure
out when it must resize itself.
typedef struct {
void **data;
int capacity;
int size;
} vector;
- (12 points) You have a circular queue with a capacity of 8 elements. For each
of the
following two subparts, show what the contents of the array will
be after the specified sequence of enqueue and dequeue operations,
and show where the front and back indices will point.
If an array entry is empty, either because it has never been filled,
or because its value has been dequeued, then leave its box blank.
-
enqueue(6);
enqueue(5);
enqueue(3);
enqueue(2);
dequeue();
dequeue();
0 1 2 3 4 5 6 7
-------------------------------------------------
| | | | | | | | |
| | | 3 | 2 | | | | |
-------------------------------------------------
^ ^
front back
- Now you receive the following additional set of
enqeueue and dequeue
operations. Show the contents of the array and where the front
and back pointers point after these additional operations have
been applied to the queue (i.e., do not start from scratch but
build on the queue that you started in the previous part).
enqueue(7);
enqueue(8);
enqueue(2);
enqueue(11);
dequeue();
dequeue();
enqueue(9);
0 1 2 3 4 5 6 7
-------------------------------------------------
| | | | | | | | |
| 9 | | | | 7 | 8 | 2 | 11 |
-------------------------------------------------
^ ^
back front
- (16 points) Consider the following declarations and function:
push(int i, Stack *s); // push an integer i onto s
int pop(Stack *s); // pop an integer off s and return its value
bool isEmpty(Stack *s); // return true if the s is empty and false otherwise
int mystery(Stack *stack) {
char token;
int count = 0;
int x;
while (scanf("%c", &token) != EOF) {
if (token == '{') {
count++;
push(count, stack);
}
else if (token == '}') {
if (isEmpty(stack))
return 0;
else
x = pop(stack);
}
// any token other than a { or a } is ignored
}
if (isEmpty(stack)) {
return -1;
}
else
return pop(stack);
}
Suppose the input is:
{ hi { brad { how } { are } { you
- What is the contents of stack after the second } is read?
Some of your entries may be empty. Fill the stack from bottom to top:
______________
| |
| |
--------------
| |
| |
--------------
| |
| |
--------------
| |
| 2 |
--------------
| |
| 1 |
--------------
- Given this input, what does mystery return? 5
- mystery is checking something. What is it checking for?
It is checking to see whether or not the curly braces are balanced .
- mystery
returns three different values--0, -1, or x--depending on the outcome
of its check. How would your calling program interpret each of these
return values? That is, if your program had to print a message
explaining the meaning of the return value, what would it say. Be
concise (you may want to defer this question to the end of the test
because you might have to devise a couple sample input sequences to
figure it out and that could take some time).
- 0: unmatched right brace
- -1: braces are matched
- x: number of the first unmatched left brace
- (16 points) Behold the following struct, which contains a union:
struct account {
int type;
char name[30];
double balance;
union {
struct {
int check_num;
char *checks;
} checking;
struct {
double interest_rate;
double quarterly_interest;
double annual_interest;
} savings;
} acct_info;
};
- 24: How many bytes will be allocated to just the union field (i.e., acct_info)?
You should make
the following assumptions:
- ints and pointers are 4 bytes
- doubles are 8 bytes
- a single char takes 1 byte
- fields are packed together as tightly as possible and word
alignment is ignored.
- 66: How many bytes will be allocated to the entire struct (i.e., the
account struct)?
- Suppose I have the declaration:
struct account y;
Write expressions to:
- copy the string "bvz" to the name field.
strcpy(y.name, "bvz");
- assign 30 to the check_num field of checking.
y.checking.check_num = 30;
- copy the string "Smiley" to the checks field of checking.
y.checking.checks = strdup("Smiley");
- assign .05 to the annual_interest field.
y.savings.annual_interest = .05;
- (20 points)
You are given a singly linked list, L, of integers and an array
P,
containing integer indices sorted in ascending order. The function
createList(L, P, Psize)
will create and return a new list that contains the elements in L
that correspond to the
positions specified by P. For
instance, if P = 1, 3, 4, 6, and L is the list:
L -> 6 -> 20 -> 8 -> 25 -> 30 -> 41 -> 66 -> 10 -> 20
then your new list will be:
new_list -> 20 -> 25 -> 30 -> 66
which, using zero-based indexing, correspond to the elements at
index locations 1, 3, 4, and 6 in L.
Write the procedure createElements(L,P,Psize) using my sllist library.
In case you have forgotten, I have attached the interface for
the sllist library to the end of the exam.
Here is some helpful information:
- L is a pointer to an Sllist.
- P is a pointer to an integer array.
- Psize is the size of the integer array.
- The return value is a pointer to an Sllist.
- You may assume that P does not
have integer indices that are
larger than the number of elements in L. Hence you do not need
to check whether you have run past the end of L.
- For maximum points, you should take advantage of the fact that
P is in ascending order and traverse L only once. If you cannot
figure out how to do this, you can traverse L multiple times for
16 points.
- You need to show me your function declaration and definition.
Do not show me include files, statements to read data,
etc. L and P have been prepared for you by the calling function.
Sllist *createList(Sllist *L, int *P, int Psize) {
Sllist_Node *n;
Sllist *new_list;
int i, j;
j = 0;
new_list = new_sllist();
n = sll_first(L);
// iterate through the elements of P. Since we know that the elements
// of P are in ascending order, we can traverse forward from the node
// that we are currently at in L to the next node specified by P.
// I used a secondary index, j, that is incremented until it has reached
// the appropriate node in L, and then I add that node to the new list
// that I am creating.
for (i = 0; i < Psize; i++) {
while (j < P[i]) {
n = sll_next(n);
j++;
}
sll_append(new_list, sll_val(n));
}
return new_list;
}
Sllist Library
- Sllist *new_sllist(): Allocates and returns a container
object for a new singly linked list.
- void free_sllist(Sllist *l): Destroys the list, calling free()
on all allocated memory in the list. The list does not have to
be empty.
- Sllist_Node *sll_prepend(Sllist *l, void *val):
Adds a new node at the beginning of the list.
This node's value is val, and a pointer to the node is returned.
- Sllist_Node *sll_append(Sllist *l, void *val):
Adds a new node to the end of the list.
This node's value is val, and a pointer to the node is returned.
- Sllist_Node *sll_insert_after(Sllist *l, Sllist_Node *n, void *val):
Adds a new node to list l
right after the specified node, n.
This node's value is val, and a pointer to the node is returned.
A pointer to the list's container object must be passed as an argument
because the
insertion algorithm may have to update the list's tail pointer, if
the new node becomes the last element in the list.
- Sllist_Node *sll_first(Sllist *l): Returns a pointer to the first node
in the list. If the list is empty, this returns NULL.
- Sllist_Node *sll_last(Sllist *l): Returns a pointer to the last node
in the list. If the list is empty, this returns NULL.
- Sllist_Node *sll_next(Sllist_Node *n): Returns a pointer to the next node
in the list after n. If n is the last node on the list,
then sll_next(n) returns NULL.
- void *sll_val(Sllist_Node *n): Returns a pointer to
n's value. You will need to cast this pointer to the appropriate
type.
- int sll_empty(Sllist l): Returns whether l is empty.