Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Resources

Throughout the semester, I will fill this webpage with useful resources. Most (if not all) of these resources will be freely available online.

In-Class LeetCode

In this section, I will post the LeetCode problems we have tackled in class, as well as the code we wrote to solve the problems. This section will continue to grow throughout the semester. Note that it is posted in reverse chronological order (most recent problems are posted first).

Minimum Cost to Cut a Stick

Link; (Dynamic Programming, Nov. 25, 2025)

Top-down DP (Memoization)

class Solution {
    vector<vector<int>> dp;
public:
    int computeCost(int i, int j, int start, int end, vector<int>& cuts) {
        if(i > j) return 0;
        if(dp[i][j] != 0) return dp[i][j];

        int minCost = INT_MAX;
        for(int k = i; k <= j; k++) {
            int left = computeCost(i, k-1, start, cuts[k], cuts);
            int right = computeCost(k+1, j, cuts[k], end, cuts);
            int my_cost = end - start;
            minCost = min(minCost, left+right+my_cost);
        }
        dp[i][j] = minCost;
        return dp[i][j];
    }

    int minCost(int n, vector<int>& cuts) {
        int s = cuts.size();
        sort(cuts.begin(), cuts.end());
        dp = vector<vector<int>>(s, vector<int>(s));
        return computeCost(0, s-1, 0, n, cuts);
    }
};

Bottom-up DP (Tabulation)

class Solution {
    vector<vector<int>> dp;
public:
    int minCost(int n, vector<int>& cuts) {
        int s = cuts.size();
        sort(cuts.begin(), cuts.end());
        dp = vector<vector<int>>(s, vector<int>(s));

        // tabulation
        for(int i = s-1; i>=0; --i) {
            for(int j = i; j < s; ++j) {
                int minCost = INT_MAX;
                int start = 0;
                if(i > 0) start = cuts[i-1];
                int end = n;
                if(j < s-1) end = cuts[j+1];

                for(int k = i; k <= j; ++k) {
                    int left = 0;
                    int right = 0;
                    if(k > i) left = dp[i][k-1];
                    if(k < j) right = dp[k+1][j];
                    minCost = min(minCost, left+right+(end-start));
                }
                dp[i][j] = minCost;
            }
        }
        return dp[0][s-1];
    }
};

Binary Tree Maximum Path Sum

Link; (Dynamic Programming, Nov. 20, 2025)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int global_max = numeric_limits<int>::min();
    int findMax(TreeNode* root) {
        if(root == nullptr) return 0;
        int left = max(0, findMax(root->left));
        int right = max(0, findMax(root->right));
        global_max = max(global_max, root->val + left + right);

        return root->val + max(left, right);
    }
    int maxPathSum(TreeNode* root) {
        findMax(root);
        return global_max;
    }
};

Dungeon Game

Link; (Dynamic Programming, Nov. 18, 2025)

class Solution {
public:
    int calculateMinimumHP(vector<vector<int>>& dungeon) {
        int m = dungeon.size();
        int n = dungeon[0].size();

        vector<vector<int>> dp(m+1, vector<int>(n+1, INT_MAX));

        dp[m][n-1] = 1;
        dp[m-1][n] = 1;

        for(int i = m-1; i >= 0; --i) {
            for(int j = n-1; j >= 0; --j) {
                int val = min(dp[i+1][j], dp[i][j+1]) - dungeon[i][j];

                dp[i][j] = (val <= 0) ? 1 : val;

                // if(val <= 0) {
                //     dp[i][j] = 1;
                // }
                // else {
                //     dp[i][j] = val;
                // }
            }
        }
        return dp[0][0];
    }
};

Binary Tree Cameras

Link; (Dynamic Programming + Trees, Nov. 13, 2025)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> memoization;
    int dp(TreeNode* root, int state) {
        if(root == nullptr) {
            return state==2;
        }
        else if(memoization[root->val][state]!= -1) {
            return memoization[root->val][state];
        }
        else {
            int cur_val = INT_MAX;
            switch(state) {
                case 2: {
                    cur_val = min(cur_val, 1+dp(root->left,1) + dp(root->right,1));
                    break;
                }
                case 1: {
                    cur_val = min(cur_val, dp(root->left,0) + dp(root->right,0));
                    cur_val = min(cur_val, 1+dp(root->left,1)+dp(root->right,1));
                    break;
                }
                default: { // case 0
                    cur_val = min(cur_val,1+dp(root->left,1)+dp(root->right,1));
                    cur_val = min(cur_val, dp(root->left,2)+dp(root->right,0));
                    cur_val = min(cur_val, dp(root->left,0)+dp(root->right,2));
                    break;
                }
            }
            memoization[root->val][state] = cur_val;
            return memoization[root->val][state];
        }
    }

    void dfs(int& n, TreeNode* root) {
        if(root == nullptr) return;
        root->val=n++;
        dfs(n,root->left);
        dfs(n, root->right);
    }

    int minCameraCover(TreeNode* root) {
        if(root == nullptr) return 0;
        int n = 0;
        dfs(n, root);
        memoization = vector<vector<int>>(n);
        for(int i = 0; i < n; ++i) {
            memoization[i] = {-1, -1, -1};
        }

        return dp(root,0);
    }
};

Binary Trees With Factors

Link; (Dynamic Programming + Trees, Nov. 13, 2025)

class Solution {
public:
    int numFactoredBinaryTrees(vector<int>& arr) {
        long result = 0;
        long mod = pow(10,9) + 7;

        unordered_map<int, long> tablu;
        sort(arr.begin(), arr.end()); //arr[i], arr[j] for j < i
        for(int i = 0; i < arr.size(); ++i) {
            tablu[arr[i]] = 1;
            for(int j = 0; j < i; ++j) {
                // [2, 4, 8, 16]
                if(arr[i] % arr[j] == 0) {
                    tablu[arr[i]] = (tablu[arr[i]] + tablu[arr[j]]*tablu[arr[i]/arr[j]]) % mod;
                }
            }
            result = (result + tablu[arr[i]]) % mod;
        }
        return result;
    }
};

Fibonacci Number

Link; (Dynamic Programming, Nov. 11, 2025)

class Solution {
public:
    int fib_recurse(int n) {
        // bad exp time!
        if(n == 0 || n == 1) {
            return n;
        }
        else return fib_recurse(n-1) + fib_recurse(n-2);
    }

    int fib_memo_help(vector<int>& table, int n) {
        if(n == 0) {
            return 0;
        }
        if(table[n] > 0) {
            return table[n];
        }
        table[n] = fib_memo_help(table, n-1) + fib_memo_help(table, n-2);
        return table[n];
    }
    
    int fib_top_down_memo(int n) {
        vector<int> table(n+1,0);
        table[1] = 1;
        return fib_memo_help(table,n);
    }


    int fib_bottom_up(int n) {
        if(n == 0 || n == 1) {
            return n;
        }
        int val0 = 0;
        int val1 = 1;
        for(int i = 2; i < n+1; ++i) {
            int tmp = val1;
            val1 = val1+val0;
            val0 = tmp;
        }

        return val1;
    }


    int fib(int n) {
        return fib_bottom_up(n);
    }
};

Serialize and Deserialize Binary Tree

Link (Trees, Nov. 6, 2025)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Codec {
public:
    const string null = "null";
    // Encodes a tree to a single string.

    string val_to_str(int val) {
        int shifted_val = 1000+val;
        string out = to_string(shifted_val);
        while(out.size() < 4) {
            out = "0" + out;
        }
        return out;
    }

    int str_to_val(string str) {
        // assumes str is of length 4
        int val = stoi(str);
        val = val-1000;
        return val;
    }

    string serialize(TreeNode* root) {
        if(root == nullptr) return null;
        string out = "";
        // construct pre-order DFS string
        out += val_to_str(root->val);
        out += serialize(root->left);
        out += serialize(root->right);
        return out;
    }

    TreeNode* deserialize_help(string& data, unsigned int& start_pos) {
        string curr = data.substr(start_pos,4);
        //data.erase(0,4);
        if(curr == null) return nullptr;
        TreeNode* root = new TreeNode(str_to_val(curr));
        start_pos += 4;
        root->left = deserialize_help(data, start_pos);
        start_pos +=4;
        root->right = deserialize_help(data, start_pos);
        return root;
    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        //string data_copy(data);
        unsigned int start = 0;
        return deserialize_help(data, start);
    }
};

// Your Codec object will be instantiated and called as such:
// Codec ser, deser;
// TreeNode* ans = deser.deserialize(ser.serialize(root));

Tree of Coprimes

Link (Trees, Nov. 4, 2025)

class Solution {
public:
    vector<vector<int>> tree;
    unordered_map<int, vector<int>> coprimes;
    unordered_map<int, vector<pair<int, int>>> path;
    
    void DFS(int curr_id, int curr_depth, int parent_id, vector<int>& nums, vector<int>& ans) {
        // check if nums[curr_id] is coprime to anything in the current path
        int max_depth = -1;
        for(int val: coprimes[nums[curr_id]]) {
            vector<pair<int,int>>& ancestors = path[val];
            if(!ancestors.empty() && ancestors.back().second > max_depth) {
                max_depth = ancestors.back().second;
                ans[curr_id] = ancestors.back().first;
            }
        }

        path[nums[curr_id]].push_back({curr_id, curr_depth});

        for(int child_id : tree[curr_id]) {
            if(child_id != parent_id) {
                DFS(child_id, curr_depth+1, curr_id, nums, ans);
            }
        }

        path[nums[curr_id]].pop_back();
    }

    vector<int> getCoprimes(vector<int>& nums, vector<vector<int>>& edges) {
        int n = nums.size();
        vector<int> ans = vector<int>(n, -1);
        tree = vector<vector<int>>(n);

        // Build the tree
        for(const auto edge: edges) {
            tree[edge[0]].push_back(edge[1]);
            tree[edge[1]].push_back(edge[0]);
        }

        unordered_set<int> set(nums.begin(), nums.end());
        for(int n1 : set) {
            for(int n2 : set) {
                if(gcd(n1, n2) == 1) coprimes[n1].push_back(n2);
            }
        }
        DFS(0, 0, -1, nums, ans);
        return ans;
    }
};

Binary Tree Level Order Traversal

Link (Trees, Oct. 30, 2025)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        
        if(root == nullptr) return {};

        queue<TreeNode*> bfs_queue;
        bfs_queue.push(root);
        vector<vector<int>> output {};

        while(!bfs_queue.empty()) {
            int num_in_lvl = bfs_queue.size();
            vector<int> curr_lvl_order {};
            
            while(num_in_lvl > 0) {
                TreeNode* curr = bfs_queue.front();
                bfs_queue.pop();
                --num_in_lvl;
                curr_lvl_order.push_back(curr->val);
                
                if(curr->left != nullptr) {
                    bfs_queue.push(curr->left);
                }
                if(curr->right != nullptr) {
                    bfs_queue.push(curr->right);
                }
            }
            output.push_back(curr_lvl_order);
        }

        return output;
    }
};

Construct Binary Tree from Inorder and Postorder Traversal

Link (Trees, Oct. 16, 2025)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    // we'll use a hashmap so we don't have to search through the inorder vector every time!
    unordered_map<int, int> hash_map;
    // int_end is inclusive; 
    TreeNode* buildSubtree(
        vector<int>& inorder, 
        vector<int>& postorder, 
        int in_start, 
        int in_end, 
        int& post_root_index
    ) {
        if(in_start > in_end) return nullptr;

        TreeNode* root = new TreeNode(postorder[post_root_index--]);
        int inorder_root_index = hash_map[root->val];

        int right_size = in_end - inorder_root_index;

        root->right = buildSubtree(inorder, postorder, inorder_root_index+1, in_end, post_root_index);
        root->left = buildSubtree(inorder, postorder, in_start, inorder_root_index-1, post_root_index);
        
        return root;
    }

    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        for(int i = 0; i < inorder.size(); ++i) {
            hash_map[inorder[i]] = i;
        }
        int n = inorder.size();
        int post_root_index = n-1;
        return buildSubtree(inorder, postorder, 0, n-1, post_root_index);
    }
};

Minimum Height Trees

Link (Trees, Oct. 14, 2025)

class Solution {
public:
    vector<int> findMinHeightTrees(int n, vector<vector<int>>& edges) {
        if(n == 1) return vector<int>{0};
        
        vector<vector<int>> tree(n);
        vector<int> degree(n, 0);
        for(vector<int> edge: edges) {
            tree[edge[0]].push_back(edge[1]);
            tree[edge[1]].push_back(edge[0]);

            ++degree[edge[0]];
            ++degree[edge[1]];
        }

        vector<int> output;

        queue<int> deg_1_queue;

        for(int i = 0; i < n;  ++i) {
            if(degree[i] == 1) deg_1_queue.push(i);
        }

        while(!deg_1_queue.empty()) {
            int queue_size = deg_1_queue.size();
            output.clear();

            while(queue_size) {
                int curr = deg_1_queue.front();
                deg_1_queue.pop();

                output.push_back(curr);

                for(int i = 0; i < tree[curr].size(); ++i) {
                    --degree[tree[curr][i]];
                    if(degree[tree[curr][i]]==1) {
                        deg_1_queue.push(tree[curr][i]);
                    }
                }

                --queue_size;
            }
        }
        return output;
    }
};

Shortest Path with Alternating Colors

Link (Graphs, Oct. 14, 2025) DFS approach.

class Solution {
public:
    vector<vector<int>> red_graph;
    vector<vector<int>> blue_graph;
    vector<int> shortest_red;
    vector<int> shortest_blue;


    void calcShortest(int node, int path_len, char edge_color) {
        switch(edge_color) {
            case 'b':
                if(shortest_blue[node] == -1 || path_len < shortest_blue[node]) {
                    shortest_blue[node] = path_len;
                    for(int i = 0; i < red_graph[node].size(); ++i) {
                        calcShortest(red_graph[node][i], path_len+1, 'r');
                    }
                }
                break;

            case 'r':
                if(shortest_red[node] == -1 || path_len < shortest_red[node]) {
                    shortest_red[node] = path_len;
                    for(int i = 0; i < blue_graph[node].size(); ++i) {
                        calcShortest(blue_graph[node][i], path_len+1, 'b');
                    }
                }
                break;

            default:
                throw std::invalid_argument( "received incorrect edge color" );
        }
        
    }


    vector<int> shortestAlternatingPaths(int n, vector<vector<int>>& redEdges, vector<vector<int>>& blueEdges) {
        red_graph = vector<vector<int>>(n);
        blue_graph = vector<vector<int>>(n);
        shortest_red = vector<int>(n);
        shortest_blue = vector<int>(n);

        // these will track the shortest paths from node 0 to node i when entering
        // node i from the respective color
        shortest_red[0] = 0;
        shortest_blue[0] = 0;

        // init all path lengths
        for(int i = 1; i < n; ++i) {
            shortest_red[i] = -1;
            shortest_blue[i] = -1;
        }
        

        // construct the graphs
        for(vector<int> redge: redEdges) {
            red_graph[redge[0]].push_back(redge[1]);
        }

        for(vector<int> bedge: blueEdges) {
            blue_graph[bedge[0]].push_back(bedge[1]);
        }

        for(int i = 0; i < red_graph[0].size(); ++i) {
            calcShortest(red_graph[0][i], 1, 'r');
        }
        for(int i = 0; i < blue_graph[0].size(); ++i) {
            calcShortest(blue_graph[0][i], 1, 'b');
        }

        vector<int> output(n);
        output[0] = 0;
        for(int i = 1; i < n; ++i) {
            if(shortest_red[i] == -1 && shortest_blue[i] == -1) {
                output[i] = -1;
            }
            else if(shortest_red[i] == -1) {
                output[i] = shortest_blue[i];
            }
            else if(shortest_blue[i] == -1) {
                output[i] = shortest_red[i];
            }
            else {
                output[i] = (shortest_red[i] <= shortest_blue[i]) ? shortest_red[i] : shortest_blue[i];
            }
        }
        return output;
    }
};

BFS approach.

class Solution {
public:
    vector<vector<int>> red_graph;
    vector<vector<int>> blue_graph;
    vector<int> shortest_red;
    vector<int> shortest_blue;



    vector<int> shortestAlternatingPaths(int n, vector<vector<int>>& redEdges, vector<vector<int>>& blueEdges) {
        red_graph = vector<vector<int>>(n);
        blue_graph = vector<vector<int>>(n);
        shortest_red = vector<int>(n);
        shortest_blue = vector<int>(n);

        // these will track the shortest paths from node 0 to node i when entering
        // node i from the respective color
        shortest_red[0] = 0;
        shortest_blue[0] = 0;

        // init all path lengths
        for(int i = 1; i < n; ++i) {
            shortest_red[i] = -1;
            shortest_blue[i] = -1;
        }
        

        // construct the graphs
        for(vector<int> &redge: redEdges) {
            red_graph[redge[0]].push_back(redge[1]);
        }

        for(vector<int> &bedge: blueEdges) {
            blue_graph[bedge[0]].push_back(bedge[1]);
        }


        // BFS approach
        queue<vector<int>> bfs_queue;

        // 0 = red; 1 = blue
        
        bfs_queue.push({0, 0});
        bfs_queue.push({0, 1});
        

        while(!bfs_queue.empty()) {
            vector<int> curr_list = bfs_queue.front();
            bfs_queue.pop();
            int curr_node = curr_list[0];
            int curr_color = curr_list[1];
            
            // if coming from red, search all blue
            if(curr_color == 0) {
                int distance = shortest_red[curr_node];
                for(int neigh: blue_graph[curr_node]) {
                    if(shortest_blue[neigh] == -1 || distance+1 < shortest_blue[neigh]) {
                        shortest_blue[neigh] = distance + 1;
                        bfs_queue.push({neigh, 1});
                    }
                }
            }
            else { // if coming from blue, search all red
                int distance = shortest_blue[curr_node];
                for(int neigh: red_graph[curr_node]) {
                    if(shortest_red[neigh] == -1 || distance+1 < shortest_red[neigh]) {
                        shortest_red[neigh] = distance + 1;
                        bfs_queue.push({neigh, 0});
                    }
                }
            }
        }

        vector<int> output(n);
        output[0] = 0;
        for(int i = 1; i < n; ++i) {
            if(shortest_red[i] == -1 && shortest_blue[i] == -1) {
                output[i] = -1;
            }
            else if(shortest_red[i] == -1) {
                output[i] = shortest_blue[i];
            }
            else if(shortest_blue[i] == -1) {
                output[i] = shortest_red[i];
            }
            else {
                output[i] = (shortest_red[i] <= shortest_blue[i]) ? shortest_red[i] : shortest_blue[i];
            }
        }
        return output;
    }
};

Cheapest Flights within K Stops

Link (Graphs, Oct. 9, 2025)

const int infinity = std::numeric_limits<int>::max();
struct Node {
    int id;
    int price;
    int stop_num;
    vector<Node> neighbors;
    bool operator> (const Node&) const;
    bool operator>=(const Node&) const;
    bool operator==(const Node&) const;
    bool operator< (const Node&) const;
    bool operator<=(const Node&) const;
    bool operator!=(const Node&) const;
};

bool Node::operator> (const Node& node) const {return price > node.price; }
bool Node::operator>=(const Node& node) const {return price >= node.price; }
bool Node::operator==(const Node& node) const {return price == node.price; }
bool Node::operator< (const Node& node) const {return price < node.price; }
bool Node::operator<=(const Node& node) const {return price <= node.price; }
bool Node::operator!=(const Node& node) const{return price != node.price; }

class Solution {
public:
    int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int k) {
        vector<Node> graph;
        for(int i = 0; i < n; ++i) {
            graph.push_back(Node(i, infinity, infinity, {}));
        }
        for(vector<int> edge: flights) {
            graph[edge[0]].neighbors.push_back(Node(edge[1], edge[2], 0, {}));
        }

        priority_queue<Node, std::vector<Node>, std::greater<Node>> min_queue;

        min_queue.push(Node(src, 0, 0, {}));

        while(!min_queue.empty()) {
            Node curr = min_queue.top();
            min_queue.pop();

            if(curr.id == dst) return curr.price;
            if(curr.stop_num > k || curr.stop_num > graph[curr.id].stop_num) continue;

            graph[curr.id].stop_num = curr.stop_num;

            for(Node neigh: graph[curr.id].neighbors) {
                min_queue.push(Node(neigh.id, curr.price+neigh.price, curr.stop_num+1));
            }
        }
        return -1;
    }
};

Alternative, more elegant solution from LeetCode contributor.

const int infinity = std::numeric_limits<int>::max();
class Solution {
public:
    int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int k) {
        vector<vector<vector<int>>> graph(n);
        for(vector<int> edge: flights) {
            // graph[i] is a vector<vector<int>>
            // graph[i][j] is a vector<int> of the form: { to_id, price }
            graph[edge[0]].push_back({edge[1], edge[2]});
        }

        // track current number of stops of each node
        vector<int> num_stops(n, infinity);

        // nodes are stored in the form: { price, id, num_stops}
        priority_queue<array<int,3>, vector<array<int,3>>, greater<>> min_queue;

        min_queue.push({0, src, 0});

        while(!min_queue.empty()) {
            auto [price, id, stops] = min_queue.top();
            min_queue.pop();

            if(id == dst) return price;

            if(stops > k || stops > num_stops[id]) continue;

            num_stops[id] = stops;

            for(int i = 0; i < graph[id].size(); ++i) {
                min_queue.push({price + graph[id][i][1], graph[id][i][0], stops+1});
            }
        }
        return -1;
    }
};

Course Schedule II

Link (Graphs, Oct. 2, 2025)

Note that this also solves Course Schedule.

class Solution {
public:
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
        
        // First, make graph via adj list!
        vector<vector<int>> graph(numCourses);

        vector<int> visited(numCourses);

        // Our graph representation will be a predecessor representation. That is,
        // graph[i] is a list / hashmap of all the nodes j such that (j,i) is an edge.
        // In other words, graph[i] contains all courses j that are a prereq for i.
        for(vector<int> prereq: prerequisites) {
            graph[ prereq[0] ].push_back( prereq[1] );
        }

        // Now, we need to topologically sort the graph. You can do this via BFS or DFS.

        // vector representing the final ordering
        vector<int> order {};

        // BFS approach
        queue<int> bfs_queue {};

        // we need to find all nodes with no predecessors so we can start our sorting algorithm
        for(int i = 0; i < numCourses; ++i) {
            if(graph[i].empty()) { 
                bfs_queue.push(i);
                visited[i] = 1;
            }
        }

        //if(bfs_queue.empty()) return order; // return empty list!

        while(!bfs_queue.empty()) {
            int curr = bfs_queue.front();
            bfs_queue.pop();

            order.push_back(curr);

            for(int i = 0; i < numCourses; ++i) {
                if(i == curr) continue;
                for(int j = 0; j < graph[i].size(); ++j) {
                    if(graph[i][j] == curr) {
                        graph[i].erase(graph[i].begin()+j);
                        break;
                    }
                }

                if(graph[i].empty() && !visited[i]) {
                    bfs_queue.push(i);
                    visited[i] = 1;
                }
            }
        }

        if(order.size() < numCourses) return vector<int>();

        return order;
    }
};

Number of Islands

Link (Graphs, Sept. 30 and Oct. 2, 2025)

DFS Approach.

class Solution {
public:
    // Return the index of pair (row, col) in row-major order
    int pair_to_index(const int row, const int col, const int num_cols) {
        return (num_cols*row + col);
    }

    void recursive_dfs(vector<vector<char>>& grid, int num_rows, int num_cols, int i, int j, vector<bool>& visited) {
        
        // visit the current node
        visited[pair_to_index(i,j, num_cols)] = true;

        // visit in clockwise order starting from the top
        // visit north
        if(i > 0 && grid[i-1][j] == '1' && !visited[pair_to_index(i-1,j, num_cols)]) {
            recursive_dfs(grid, num_rows, num_cols, i-1, j, visited);
        }

        // visit east
        if(j < num_cols-1 && grid[i][j+1] == '1' && !visited[pair_to_index(i,j+1, num_cols)]) {
            recursive_dfs(grid, num_rows, num_cols, i, j+1, visited);
        }

        // visit south
        if(i < num_rows-1 && grid[i+1][j] == '1' && !visited[pair_to_index(i+1,j, num_cols)]) {
            recursive_dfs(grid, num_rows, num_cols, i+1, j, visited);
        }

        // visit west
        if(j > 0 && grid[i][j-1] == '1' && !visited[pair_to_index(i,j-1, num_cols)]) {
            recursive_dfs(grid, num_rows, num_cols, i, j-1, visited);
        }

        return;
    }

    int numIslands(vector<vector<char>>& grid) {

        int m = grid.size();
        int n = grid[0].size();

        int num_islands = 0;

        int total_size = m * n;

        vector<bool> visited(total_size);
       
        
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                if(grid[i][j] == '1' && !visited[pair_to_index(i, j, n)]) {
                    // recursive DFS
                    recursive_dfs(grid, m, n, i, j, visited);
                    ++num_islands;
                }
            }
        }
        return num_islands;
    }
};

BFS Approach.

class Solution {
public:
    // Return the index of pair (row, col) in row-major order
    int pair_to_index(const int row, const int col, const int num_cols) {
        return (num_cols*row + col);
    }
    
    int index_to_row(const int index, const int num_cols) {
        return index / num_cols;
    }

    int index_to_col(const int index, const int num_cols) {
        return index % num_cols;
    }


    int numIslands(vector<vector<char>>& grid) {

        int m = grid.size();
        int n = grid[0].size();

        int num_islands = 0;

        int total_size = m * n;

        vector<bool> visited(total_size);

        queue<int> bfs_queue;
       
        
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                if(grid[i][j] == '1' && !visited[pair_to_index(i, j, n)]) {

                    visited[pair_to_index(i,j,n)] = true;
                    // Loop BFS
                    bfs_queue.push(pair_to_index(i,j,n));
                    while(!bfs_queue.empty()) {
                        int index = bfs_queue.front();
                        bfs_queue.pop();

                        int row = index_to_row(index, n);
                        int col = index_to_col(index, n);

                        // visit the node
                        visited[index] = true;

                        // do BFS, clockwise starting north
                        // visit north
                        if(row > 0 && grid[row-1][col] == '1' && !visited[pair_to_index(row-1, col, n)]) {
                            visited[pair_to_index(row-1,col,n)] = true;
                            bfs_queue.push(pair_to_index(row-1, col, n));
                        }

                        // visit east
                        if(col < n-1 && grid[row][col+1] == '1' && !visited[pair_to_index(row,col+1, n)]) {
                            visited[pair_to_index(row,col+1,n)] = true;
                            bfs_queue.push(pair_to_index(row, col+1, n));
                        }

                        // visit south
                        if(row < m-1 && grid[row+1][col] == '1' && !visited[pair_to_index(row+1,col, n)]) {
                            visited[pair_to_index(row+1,col,n)] = true;
                            bfs_queue.push(pair_to_index(row+1, col, n));
                        }

                        // visit west
                        if(col > 0 && grid[row][col-1] == '1' && !visited[pair_to_index(row,col-1, n)]) {
                            visited[pair_to_index(row,col-1,n)] = true;
                            bfs_queue.push(pair_to_index(row, col-1, n));
                        }
                    }

                    ++num_islands;
                }
            }
        }

        return num_islands;
    }
};

Clone Graph

Link (Graphs, Sept. 25, 2025)

/*
// Definition for a Node.
class Node {
public:
    int val;
    vector<Node*> neighbors;
    Node() {
        val = 0;
        neighbors = vector<Node*>();
    }
    Node(int _val) {
        val = _val;
        neighbors = vector<Node*>();
    }
    Node(int _val, vector<Node*> _neighbors) {
        val = _val;
        neighbors = _neighbors;
    }
};
*/


class Solution {
public:
    Node* cloneGraph(Node* node) {
        queue<Node*> bfs_queue {};
        unordered_map<int, Node*> cloned_nodes {};

        if(node == nullptr) {
            return nullptr;
        }
        
        Node* clone_start = new Node(node->val);
        cloned_nodes[clone_start->val] = clone_start;

        bfs_queue.push(node);

        while(!bfs_queue.empty()) {
            Node* curr = bfs_queue.front();
            bfs_queue.pop();

            for(Node* neigh: curr->neighbors) {
                cout << neigh->val << ", ";
                if(!cloned_nodes[neigh->val]) {
                    cloned_nodes[neigh->val] = new Node(neigh->val);
                    bfs_queue.push(neigh);
                }
                cloned_nodes[curr->val]->neighbors.push_back(cloned_nodes[neigh->val]);
            }
        }

        return clone_start;
    }
};

Group Anagrams

Link (Hashing, Sept. 18, 2025)

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        // step 1: given a string s, make new string s' where s' is char's of s sorted
        // example:
        // "eat" -> "aet"

        // step 2: sorted s' is your key, hash table is a table of arrays
        // unordered_map<string, string[]> H;
        // H[s'].append(s);

        // example: "eat"  -> "aet"
        // H["aet"].push("eat");

        // issue: what if hash("aet") = hash("efg")  

        unordered_map<string, vector<string>> map;

        for(string str : strs) {
            string strcpy = str;
            sort(strcpy.begin(),strcpy.end());

            map[strcpy].push_back(str);
        }

        vector<vector<string>> out;

        for(auto k: map) {
            out.push_back(k.second);
        }

        return out;


    }
};

First Missing Positive

Link (Hashing, Sept. 18, 2025)

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        int i;
        for(i = 0; i < nums.size(); i++) {
            if(nums[i] <=0 or nums[i] >= nums.size()+1) nums[i] = nums.size()+1;
        }
        for(i=0; i < nums.size(); i++) {
            if(abs(nums[i]) <= nums.size()) nums[abs(nums[i])-1] = -1*abs(nums[abs(nums[i])-1]); 
        }
        for(i=0; i < nums.size(); i++) {
            if(nums[i] > 0) {
                return i+1;
            }
        }
        return nums.size()+1;
    }
};

Longest Substring Without Repeating Characters

Link (Hashing, Sept. 16, 2025)

class Solution {
public:
    int lengthOfLongestSubstring(string s) {

        short int seen_char[256] {};
        int max_length = 0;
        short int left = 0;
        short int diff = 0;

        for(short int right = 0; right < s.size(); ++right) {
            ++diff;
            if(!seen_char[s[right]]) {
                ++seen_char[s[right]];
                max_length = (diff > max_length) ? diff : max_length;
            }
            else {
                while(seen_char[s[right]]) {
                    --seen_char[s[left]];
                    ++left;
                    --diff;
                }
                ++seen_char[s[right]];
            }
        }

        return max_length;

    }
};

Longest Substring Without Repeating Characters

Link (Hashing, Sept. 11, 2025)

#include <unordered_set>
#include <math.h>

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int max_len = 0;
        unordered_set<char> char_set;
        auto left = s.begin();

        for(auto right = s.begin(); right != s.end(); right++) {
            if(char_set.count(*right) == 0) {
                char_set.insert(*right);
                max_len = max(max_len, static_cast<int>(right-left+1));
            }
            else {
                while(char_set.count(*right)) {
                    char_set.erase(*left);
                    left++;
                }
                char_set.insert(*right);
            }
        }

        return max_len;
    }
};

Two Sum

Link (Hashing, Sept. 11, 2025)

#include <unordered_map>

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> indices{-1, -1};

        unordered_map<int,int> hashmap;

        for(int i = 0; i < nums.size(); i++) {
            hashmap[nums[i]] = i;
        }

        for(int i = 0; i < nums.size(); i++) {
            int y = target - nums[i];
            if(hashmap[y] && hashmap[y] != i) {
                indices[0] = i;
                indices[1] = hashmap[y];
                break;
            }
        }

        return indices;
    }
};