Practice Final 2 Solutions


Q1) Backtracking

bool canSpell(string word, Vector<string>& cubes) {
    if (word.empty()) {
        return true;
    }
    for (int i = 0; i < cubes.size(); i++) {
        string cube = cubes[i];
        if (cube.find(word[0]) != string::npos) {
            cubes.remove(i);
            if (canSpell(word.substr(1), cubes)) {
                return true;
            }
            cubes.insert(i, cube);
        }
    }
    return false;
}


Q2) Classes

// private member variables in .h
bool* _range;
int _min, _max, _count;
// member function implementation in .cpp
RangeSet::RangeSet(int minValue, int maxValue) {
    if (minValue > maxValue) error("Invalid range!");
    _range = new bool[maxValue - minValue + 1](); // init elements to false
    _min = minValue;
    _max = maxValue;
    _count = 0;
}

RangeSet::~RangeSet() {
    delete[] _range;
}

bool RangeSet::add(int value) {
    int index = getIndex(value);
    if (index == -1 || _range[index]) {
        return false;
    }
    _range[index] = true;
    _count++;
    return true;
}

int RangeSet::getIndex(int value) const {
    if (value < _min || value > _max) {
        return -1;
    }
    return value - _min;
}

int RangeSet::count() const {
    return _count;
}


Q3) Lists

void separate(ListNode* front, PriorityQueue<ListNode*>& runs) {
    while (front != nullptr) {
        ListNode* rest = splitRun(front);
        runs.enqueue(front, front->value);  // surprisingly, can even enqueue before split
        front = rest;
    }
}

ListNode *kWayMerge(PriorityQueue<ListNode*>& runs) {
    ListNode *output = nullptr, *outputTail = nullptr;

    while (!runs.isEmpty()) {
        ListNode* cur = runs.dequeue(); // cur is smallest of remaining runs
        if (output == nullptr) {
            output = cur;               // cur is first node to go of output
        } else {
            outputTail->next = cur;     // append cur to existing output
        }
        outputTail = cur;               // cur is now tail of output
        ListNode* remainder = cur->next;
        if (remainder != nullptr) {     // re-enqueue remainder if any
            runs.enqueue(remainder, remainder->value);
        }
    }
    return output;
}

runSort is O(NlogK) where N is length of input and K is number of runs. Best-case: already-sorted input. K = 1, runtime is O(N) Worst-case: reversed-sorted input. K = N, runtime is O(NlogN)


Q4) Trees

Vector: 21 22 23 25 27 28 30
Resulting tree:   25
                 /  \
               22   28
              / \   / \
             21 23 27 30
void dismantle(BSTNode* t, Vector<int>& v) {
    if (t != nullptr) {
        dismantle(t->left, v);
        v.add(t->value);    // in-order add
        dismantle(t->right, v);
        delete t;           // post-order delete
    }
}

BSTNode *rebuild(Vector<int>& v) {
    return rebuildHelper(v, 0, v.size() - 1);
}

// start and stop indexes are _inclusive_ in this solution
BSTNode *rebuildHelper(Vector<int>& v, int start, int stop) {
    if (start > stop) return nullptr;
    int mid = (start + stop)/2;
    BSTNode* t = new BSTNode;
    t->value = v[mid];
    t->left = rebuildHelper(v, start, mid - 1);
    t->right = rebuildHelper(v, mid + 1, stop);
    return t;
}