# Cheatsheets ###### tags: `LeetCode筆記` ## :memo: 一、Introduction ### 蒐集一些對於面試及解決LeetCode問題有幫助的資源 ### 關於時間複雜度,資料結構,演算法,code template,coding面試的階段 所有內容來自於LeetCode's Interview Crash Course: Data Structures and Algorithm (LICC) ## :memo: 二、Resource * Code templates * Stages of an interview * Cheatsheets ## :memo: 三、Code templates C++ ### Two pointers: one input, opposite ends ``` int fn(vector<int>& arr) { int left = 0; int right = int(arr.size()) - 1; int ans = 0; while (left < right) { // do some logic here with left and right if (CONDITION) { left++; } else { right--; } } return ans; } ``` ### Two pointers: two inputs, exhaust both ``` int fn(vector<int>& arr1, vector<int>& arr2) { int i = 0, j = 0, ans = 0; while (i < arr1.size() && j < arr2.size()) { // do some logic here if (CONDITION) { i++; } else { j++; } } while (i < arr1.size()) { // do logic i++; } while (j < arr2.size()) { // do logic j++; } return ans; } ``` ### Sliding window ``` int fn(vector<int>& arr) { int left = 0, ans = 0, curr = 0; for (int right = 0; right < arr.size(); right++) { // do logic here to add arr[right] to curr while (WINDOW_CONDITION_BROKEN) { // remove arr[left] from curr left++; } // update ans } return ans; } ``` ### Build a prefix sum ``` vector<int> fn(vector<int>& arr) { vector<int> prefix(arr.size()); prefix[0] = arr[0]; for (int i = 1; i < arr.size(); i++) { prefix[i] = prefix[i - 1] + arr[i]; } return prefix; } ``` ### Efficient string building ``` string fn(vector<char>& arr) { return string(arr.begin(), arr.end()) } ``` ### Linked list: fast and slow pointer ``` int fn(ListNode* head) { ListNode* slow = head; ListNode* fast = head; int ans = 0; while (fast != nullptr && fast->next != nullptr) { // do logic slow = slow->next; fast = fast->next->next; } return ans; } ``` ### Reversing a linked list ``` ListNode* fn(ListNode* head) { ListNode* curr = head; ListNode* prev = nullptr; while (curr != nullptr) { ListNode* nextNode = curr->next; curr->next = prev; prev = curr; curr = nextNode; } return prev; } ``` ### Find number of subarrays that fit an exact criteria ``` int fn(vector<int>& arr, int k) { unordered_map<int, int> counts; counts[0] = 1; int ans = 0, curr = 0; for (int num: arr) { // do logic to change curr ans += counts[curr - k]; counts[curr]++; } return ans; } ``` ### Monotonic increasing stack The same logic can be applied to maintain a monotonic queue. ``` int fn(vector<int>& arr) { stack<integer> stack; int ans = 0; for (int num: arr) { // for monotonic decreasing, just flip the > to < while (!stack.empty() && stack.top() > num) { // do logic stack.pop(); } stack.push(num); } } ``` ### Binary tree: DFS (recursive) ``` int dfs(TreeNode* root) { if (root == nullptr) { return 0; } int ans = 0; // do logic dfs(root.left); dfs(root.right); return ans; } ``` ### Binary tree: DFS (iterative) ``` int dfs(TreeNode* root) { stack<TreeNode*> stack; stack.push(root); int ans = 0; while (!stack.empty()) { TreeNode* node = stack.top(); stack.pop(); // do logic if (node->left != nullptr) { stack.push(node->left); } if (node->right != nullptr) { stack.push(node->right); } } return ans; } ``` ### Binary tree: BFS ``` int fn(TreeNode* root) { queue<TreeNode*> queue; queue.push(root); int ans = 0; while (!queue.empty()) { int currentLength = queue.size(); // do logic for current level for (int i = 0; i < currentLength; i++) { TreeNode* node = queue.front(); queue.pop(); // do logic if (node->left != nullptr) { queue.push(node->left); } if (node->right != nullptr) { queue.push(node->right); } } } return ans; } ``` ### Graph: DFS (recursive) For the graph templates, assume the nodes are numbered from 0 to n - 1 and the graph is given as an adjacency list. Depending on the problem, you may need to convert the input into an equivalent adjacency list before using the templates. ``` unordered_set<int> seen; int fn(vector<vector<int>>& graph) { seen.insert(START_NODE); return dfs(START_NODE, graph); } int dfs(int node, vector<vector<int>>& graph) { int ans = 0; // do some logic for (int neighbor: graph[node]) { if (seen.find(neighbor) == seen.end()) { seen.insert(neighbor); ans += dfs(neighbor, graph); } } return ans; } ``` ### Graph: DFS (iterative) ``` int fn(vector<vector<int>>& graph) { stack<int> stack; unordered_set<int> seen; stack.push(START_NODE); seen.insert(START_NODE); int ans = 0; while (!stack.empty()) { int node = stack.top(); stack.pop(); // do some logic for (int neighbor: graph[node]) { if (seen.find(neighbor) == seen.end()) { seen.insert(neighbor); stack.push(neighbor); } } } } ``` ### Graph: BFS ``` int fn(vector<vector<int>>& graph) { queue<int> queue; unordered_set<int> seen; queue.add(START_NODE); seen.insert(START_NODE); int ans = 0; while (!queue.empty()) { int node = queue.front(); queue.pop(); // do some logic for (int neighbor: graph[node]) { if (seen.find(neighbor) == seen.end()) { seen.insert(neighbor); queue.push(neighbor); } } } } ``` ### Find top k elements with heap ``` vector<int> fn(vector<int>& arr, int k) { priority_queue<int, CRITERIA> heap; for (int num: arr) { heap.push(num); if (heap.size() > k) { heap.pop(); } } vector<int> ans; while (heap.size() > 0) { ans.push_back(heap.top()); heap.pop(); } return ans; } ``` ### Binary search ``` int binarySearch(vector<int>& arr, int target) { int left = 0; int right = int(arr.size()) - 1; while (left <= right) { int mid = left + (right - left) / 2; if (arr[mid] == target) { // do something; return mid; } if (arr[mid] > target) { right = mid - 1; } else { left = mid + 1; } } // left is the insertion point return left; } ``` ### Binary search: duplicate elements, left-most insertion point ``` int binarySearch(vector<int>& arr, int target) { int left = 0; int right = arr.size(); while (left < right) { int mid = left + (right - left) / 2; if (arr[mid] >= target) { right = mid; } else { left = mid + 1; } } return left; } ``` ### Binary search: duplicate elements, right-most insertion point ``` int binarySearch(vector<int>& arr, int target) { int left = 0; int right = arr.size(); while (left < right) { int mid = left + (right - left) / 2; if (arr[mid] > target) { right = mid; } else { left = mid + 1; } } return left; } ``` ### Binary search: for greedy problems If looking for a minimum: ``` int fn(vector<int>& arr) { int left = MINIMUM_POSSIBLE_ANSWER; int right = MAXIMUM_POSSIBLE_ANSWER; while (left <= right) { int mid = left + (right - left) / 2; if (check(mid)) { right = mid - 1; } else { left = mid + 1; } } return left; } bool check(int x) { // this function is implemented depending on the problem return BOOLEAN; } ``` If looking for a maximum: ``` int fn(vector<int>& arr) { int left = MINIMUM_POSSIBLE_ANSWER; int right = MAXIMUM_POSSIBLE_ANSWER; while (left <= right) { int mid = left + (right - left) / 2; if (check(mid)) { left = mid + 1; } else { right = mid - 1; } } return right; } bool check(int x) { // this function is implemented depending on the problem return BOOLEAN; } ``` ### Backtracking ``` int backtrack(STATE curr, OTHER_ARGUMENTS...) { if (BASE_CASE) { // modify the answer return 0; } int ans = 0; for (ITERATE_OVER_INPUT) { // modify the current state ans += backtrack(curr, OTHER_ARGUMENTS...) // undo the modification of the current state } return ans; } ``` ### Dynamic programming: top-down memoization ``` unordered_map<STATE, int> memo; int fn(vector<int>& arr) { return dp(STATE_FOR_WHOLE_INPUT, arr); } int dp(STATE, vector<int>& arr) { if (BASE_CASE) { return 0; } if (memo.find(STATE) != memo.end()) { return memo[STATE]; } int ans = RECURRENCE_RELATION(STATE); memo[STATE] = ans; return ans; } ``` ### Build a trie ``` // note: using a class is only necessary if you want to store data at each node. // otherwise, you can implement a trie using only hash maps. struct TrieNode { int data; unordered_map<char, TrieNode*> children; TrieNode() : data(0), children(unordered_map<char, TrieNode*>()) {} }; TrieNode* buildTrie(vector<string> words) { TrieNode* root = new TrieNode(); for (string word: words) { TrieNode* curr = root; for (char c: word) { if (curr->children.find(c) == curr->children.end()) { curr->children[c] = new TrieNode(); } curr = curr->children[c]; // at this point, you have a full word at curr // you can perform more logic here to give curr an attribute if you want } } return root; } ``` ## :memo: 四、Stages of an interview 1. Introductions 2. Problem statement 3. Brainstorming DS&A 4. Implementation 5. Testing & debugging 6. Explanations and follow-ups 7. Outro ## :memo: 五、Cheatsheets ![](https://hackmd.io/_uploads/BJMXLlArh.png) ![](https://hackmd.io/_uploads/S1FBUgCr2.png) ![](https://hackmd.io/_uploads/ByRUIgRB3.png) ![](https://hackmd.io/_uploads/SJID8lAr3.png) ![](https://hackmd.io/_uploads/Sknw8x0H2.png) ![](https://hackmd.io/_uploads/SkGdUlAr2.png) ![](https://hackmd.io/_uploads/ry_uUgCSn.png) ![](https://hackmd.io/_uploads/r1auLgABn.png) ![](https://hackmd.io/_uploads/r1GtUxRBn.png) ![](https://hackmd.io/_uploads/ByPKIgCBn.png) ![](https://hackmd.io/_uploads/SypFLxCrh.png) ![](https://hackmd.io/_uploads/Bk-9LxAHn.png) ![](https://hackmd.io/_uploads/SJU5IgCB2.png) ![](https://hackmd.io/_uploads/r1n5IgRHh.png) ![](https://hackmd.io/_uploads/rySiIxCHn.png) ![](https://hackmd.io/_uploads/Sk3iLxRH2.png) ![](https://hackmd.io/_uploads/HyXhIlRB2.png) ![](https://hackmd.io/_uploads/Hkoh8xAHh.png) ![](https://hackmd.io/_uploads/Syr6LeArh.png) ![](https://hackmd.io/_uploads/B1T6Ig0H3.png) ![](https://hackmd.io/_uploads/Sk8A8lCHn.png) ![](https://hackmd.io/_uploads/rJ3CUx0B3.png)