Final 3 Practice Solutions


Q1) Recursive backtracking

bool canPair(Vector<string> students, int maxOk) {
    if (maxOk < 0) {
        return false;
    }
    if (students.isEmpty()) {
        return true;
    }

    for (int i = 1; i < students.size(); i++) {
            // choose: match student[0] with student[i]
            // if score returns 0, usedOk is "true" which is int value 1
        int usedOk = (score(students[0], students[i]) == 0);
            // copy of vector without pair just matched
        Vector<string> rest = students.subList(1, i - 1) + students.subList(i + 1);
        if (canPair(rest, maxOk - usedOk)) {
            return true;
        }
    }
    return false;
}

Q2) Classes

private:
    string *_recents;
    int _capacity;
    int _totalCalls;
CallHistory::CallHistory(int capacity) {
    _capacity   = capacity;
    _recents    = new string[_capacity];
    _totalCalls = 0; 
}

CallHistory::~CallHistory() {
    delete[] _recents;
}

void CallHistory::add(string caller) {
    _recents[_totalCalls % _capacity] = caller;
    _totalCalls++;
}

string CallHistory::getRecent(int n) {
    // Invalid n would be negative, greater than allowed capacity, 
    // or within unitialized range (i.e. only 3 calls in history
    // with capacity 5, try to access 4)
    if (n < 0 || n >= _capacity || n >= _totalCalls) {
        error("Requested recent is out of range!");
    }
    return _recents[(_totalCalls - 1 - n) % _capacity];
}

int CallHistory::getTotalCalls() {
    return _totalCalls;
}

Q3) Linked lists

void transform(ListNode*& front) {
    ListNode *prev = nullptr;
    ListNode *cur = front;

    while (cur != nullptr) {
        if (cur->data < front->data) { // if move cur
            prev->next = cur->next;    // splice out 
            cur->next = front;         // push on front
            front = cur;
            cur = prev->next;          // advance cur, NOT prev
        } else {             // else not move cur
            prev = cur;      // advance BOTH cur and prev
            cur = cur->next;
        }
    }
}

Q4) Trees

void tighten(Node*& t) {
    if (t != nullptr) {
        tighten(t->left);
        tighten(t->right);

        Node *onlyChild = nullptr;
        if (t->left != nullptr && t->right == nullptr) {
            onlyChild = t->left;
        } else if (t->right != nullptr && t->left == nullptr) {
            onlyChild =  t->right;
        }
        if (onlyChild != nullptr) {  // this is improper node
            delete t;     // deallocate and replace with child
            t = onlyChild;
        }
    }
}

Q5) Short answer

Sorting.

A reverse sort directly following a forward sort will be a worst-case input for this QuickSort (data is in reverse-sorted order, pivot will partition into 1 and N-1). The worst-case input, will degenerate to O(N^2) time and the depth of call stack grows to N frames (one for each message in mailbox). When N exceeds system capacity, this results in stack overflow and program crash. The other sort operations were either not worst-case degenerate or small enough mailbox to successfully complete.

Binary Heap.

Do a linear search of array to find the DataPoint with matching label. Cost = O(N) Update priority and call one of bubble up/down to resettle DataPoint into correct position within min-heap (whether bubble up or down depends if whether priority increased/decreased). Cost = O(logN) Total runtime = O(N)

Binary Search Trees.

An in-order traversal of valid BST must visit nodes in sorted order. In-order traversal of Variant 1 or Variant 3 follows same order as original tree, thus required order is preserved and these trees are valid BSTs. In-order traversal of Variant 2 jumbles up required order, e.g. visits tan wood (root) after blue but tan wood should be penultimate accordingly to original tree. Variant 2 is not a valid BST.