Practice test for Midterm 1; solutions November 1, 2 1 1 C++ review & UML diagrams Write a function which takes a vector of int and returns true if all the elements of the vector are positive ( 0) and false otherwise: bool all_pos(vector<int> data) { for(int i = 0; i < data.size(); ++i) if(data[i] < 0) return false; return true; Draw the UML class diagram for the following set of classes: class engine { int cylinders; ; class car { int milage; engine e; ; class ford : public car { bool is_huge_pickup; ; 1
engine + cylinders : int 1 1 car + milage : int + e : engine ford + is_huge_pickup : bool What does the following function do: bool f(vector<int> x) { bool g = true; for(int i = 0; i < x.size() - 1; ++i) if(x[i] > x[i+1]) g = false; return g; Give examples of non-empty vectors, one which will cause the function to return true, and one which will cause it to return false. Checks to see if the vector is sorted ascending. True: 1,2,3,4,... False: 4,3,22,... 2
This function is intended to count the number of words (non-space sequences separated by spaces) in a string, but the lines of the function are all in the wrong order. Write them in the correct order. 1 2 w++; 3 i++; 4 int count_words(string s) { 5 return w; 6 if(s[i] == ' ' && s[i+1]!= ' ') 8 while(i < s.length() - 1) { 9 int i = 0; 1 int w = s[0] == ' '? 0 : 1; int count_words(string s) { int w = s[0] == ' '? 0 : 1; int i = 0; while(i < s.length() - 1) { if(s[i] == ' ' && s[i+1]!= ' ') w++; i++; return w; Given the following ordered array class class ordered_array {... void remove(int x); private: int* data; int current_size; int max_size; ; Implement the remove method, which should remove the element x if it exists in the array, shifting everything after it down to fill in the space. 3
void ordered_array::remove(int x) { // Find x int p = -1; for(int i = 0; i < current_size; ++i) if(data[i] == x) { pos = i; break; if(pos < 0) return; // Not found // Shift everything down for(int j = pos; j < current_size-1; ++j) data[j] = data[j+1]; current_size--; Note that this implementation will still work correctly if current_size == 0. 4
2 Big-O analysis; Vectors, lists, stacks, and queues This function searches for all occurences of a pattern p in a string s: void find_all(string s, string p) { for(int i = 0; i < s.length() - p.length(); ++i) { bool found = false; for(int j = 0; j < p.length(); ++j) if(s[i + j]!= p[j]) found = false; if(found) cout << "Found at " << i << endl; It p has length m and s has length n, and we assume that n is much larger than m, what is the big-o complexity of this function? O(mn) because the outer loop runs n m times, but if m n this is just O(n). The inner loop runs O(m) times, giving us O(mn). Here is a function that checks a vector to see if it is sorted: bool is_sorted(vector<int>& v) { for(int i = 0; i < v.size() - 1; ++i) if(v[i] > v[i+1]) return false; return true; Analyze the cost of this function, in terms of the number of comparisons C, the number of increments I, and the number of vector lookups L. What is the best case cost? What is the worst case cost? When (for what inputs) do the best/worst cases occur? The loop runs n 1 times at the worst. The best case is when the first two elements are out of order. The worst case is when the whole vector is sorted. Best case: 2C + 2L (2C because the loop does a comparison the first time, too). Worst case: (2n 1))C + 2(n 1)L Given the following implementation of vector::push_back trace through the cost of the first 1 pushbacks, if a cheap pushback (i.e., a single copy) has a cost of 1, and the initial size and capacity are. 5
void vector::push_back(int x) { if(size == capacity) { // Full, reallocate to make room int* old_data = data; data = new int[1 + capacity + capacity / 2]; // Copy everything to the new array for(int i = 0; i < capacity; ++i) data[i] = old_data[i]; capacity = 1 + capacity + capacity / 2; delete[] old_data; // Add new element data[size++] = x; PB Size Cap Cost 1 1 1 1 1 2 2 2 2 3 3 4 3 4 4 4 1 5 5 5 6 6 1 1 8 8 11 8 9 9 11 1 1 1 11 1 Given the following node definition class list { struct node { int value; node* next; ; void insert(node* prior, int x); private: node* head; 6
Implement the insert method which inserts a new value after an existing node. Be sure to handle the case when the list is empty! void list::insert(node* prior, int x) { if(prior == nullptr) head = new node{x, nullptr; else prior->next = new node{x, prior->next; Suppose we have a singly-linked list with methods head() and at(), and the above node type. The following function prints out the elements of a list: void print(list l) { for(int i = 0; i < l.size(); ++i) cout << l.at(i) << ","; What is the time complexity of this function? If necessary, rewrite the function so that its time complexity is O(n). Currently, the time complexity of this is O(n ) (because.at has time complexity O(n), and we are doing it n times). The correct loop is void print(list l) { node* n = l.head(); while(n!= nullptr) { cout << n->value << ","; n = n->next; Given the above list type, implement the stack operations push and pop: void push(list l, int x); void pop(list l); (These are written as functions so that any list can be treated as a stack.)
void push(list l, int x) { // Push to front l.head = new node{x, l.head; void pop(list l) { node* n = l.head; l.head = l.head->next; delete n; Given the above list type, implement the queue operations enqueue and dequeue: void enqueue(list l, int x); void dequeue(list l); (These are written as functions so that any list can be treated as a queue.) This list doesn t have a tail pointer, so no matter how we do it, at least one of enqueue or dequeue will be O(n). void enqueue(list l, int x) { l.head = new node{x, l.head; void dequeue(list l) { // Remove last element by finding second-to-last node* n = l.head; while(n && n->next && n->next->next) { n = n->next; // Now n is the 2nd to last element. if(n!= nullptr) { delete n->next; n->next = nullptr; 8