├── 110. Balanced Binary Tree.cpp ├── 1627. Graph Connectivity With Threshold.cpp ├── 18. 4Sum.md ├── 213. House Robber II.cpp ├── 3 sum leetcode .cpp ├── 30. Substring with Concatenation of All Words.cpp ├── 40. Combination Sum II.cpp ├── 6. ZigZag Conversion.cpp ├── 64. Minimum Path Sum.cpp ├── 80. Remove Duplicates from Sorted Array II.cpp ├── 89. Gray Code.cpp ├── 99. Recover Binary Search Tree.cpp ├── Check If a String Is a Valid Sequence from Root to Leaves Path in a Binary Tree.cpp └── README.md /110. Balanced Binary Tree.cpp: -------------------------------------------------------------------------------- 1 | //Runtime: 40 ms, faster than 5.25% of C++ online submissions for Balanced Binary Tree. 2 | //Memory Usage: 17.3 MB, less than 72.64% of C++ online submissions for Balanced Binary Tree. 3 | 4 | https://leetcode.com/problems/balanced-binary-tree/discuss/35691/The-bottom-up-O(N)-solution-would-be-better 5 | 6 | /** 7 | top-down approach 8 | time complexity: 9 | treeDepth needs O(n), and we need to calculate for every node, so total O(n^2) 10 | **/ 11 | 12 | /** 13 | * Definition for a binary tree node. 14 | * struct TreeNode { 15 | * int val; 16 | * TreeNode *left; 17 | * TreeNode *right; 18 | * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 19 | * }; 20 | */ 21 | class Solution { 22 | public: 23 | int treeDepth(TreeNode* root){ 24 | if(root == NULL) return 0; 25 | return 1 + max(treeDepth(root->left), treeDepth(root->right)); 26 | } 27 | // bool isSubtreeBalanced(TreeNode* root){ 28 | // if(root == NULL) return true; 29 | // return abs(treeDepth(root->left) - treeDepth(root->right)) <= 1; 30 | // } 31 | bool isBalanced(TreeNode* root) { 32 | if(root == NULL) return true; 33 | // return isSubtreeBalanced(root) && isSubtreeBalanced(root->left) && isSubtreeBalanced(root->right); 34 | // cout << root->val << endl; 35 | // if(abs(treeDepth(root->left) - treeDepth(root->right)) > 1){ 36 | // cout << "***" << root->val << "***" << endl; 37 | // cout << (root->left->val) << " " <<(root->right->val) << endl; 38 | // cout << treeDepth(root->left) << " " << treeDepth(root->right) << endl; 39 | // } 40 | return (abs(treeDepth(root->left) - treeDepth(root->right)) <= 1) && isBalanced(root->left) && isBalanced(root->right); 41 | } 42 | }; 43 | 44 | /** 45 | bottom-up approach 46 | time complexity: O(n) 47 | **/ 48 | 49 | //Runtime: 16 ms, faster than 99.39% of C++ online submissions for Balanced Binary Tree. 50 | //Memory Usage: 17.1 MB, less than 88.68% of C++ online submissions for Balanced Binary Tree. 51 | 52 | /** 53 | class Solution { 54 | public: 55 | int dfsHeight(TreeNode* root){ 56 | if(!root) return 0; 57 | int left = dfsHeight(root->left); 58 | if(left == -1) return -1; 59 | int right = dfsHeight(root->right); 60 | if(right == -1) return -1; 61 | if(abs(left - right) > 1) return -1; 62 | return max(left, right)+1; 63 | } 64 | bool isBalanced(TreeNode* root) { 65 | return dfsHeight(root) != -1; 66 | } 67 | }; 68 | **/ 69 | -------------------------------------------------------------------------------- /1627. Graph Connectivity With Threshold.cpp: -------------------------------------------------------------------------------- 1 | //graph 2 | //WA 3 | //57 / 66 test cases passed. 4 | //indirectly connection also considered true? 5 | class Solution { 6 | public: 7 | int gcd(int x, int y){ 8 | if(y == 0) return x; 9 | return gcd(y, x%y); 10 | } 11 | 12 | int encode(int& i, int& j, int& n){ 13 | return i*(n+1)+j; 14 | } 15 | 16 | vector areConnected(int n, int threshold, vector>& queries) { 17 | int m = queries.size(); 18 | vector ans(m, true); 19 | 20 | if(threshold == 0){ 21 | return ans; 22 | } 23 | 24 | unordered_set edges; 25 | 26 | for(int i = 1; i <= n; ++i){ 27 | for(int j = i+1; j <= n; ++j){ 28 | if(gcd(i, j) > threshold){ 29 | edges.insert(encode(i, j, n)); 30 | } 31 | } 32 | } 33 | 34 | for(int i = 0; i < m; ++i){ 35 | vector& q = queries[i]; 36 | if(q[0] > q[1]) swap(q[0], q[1]); 37 | 38 | ans[i] = (edges.find(encode(q[0], q[1], n)) != edges.end()); 39 | } 40 | 41 | return ans; 42 | } 43 | }; 44 | 45 | //graph 46 | //fix above: indirectly connected cities should also be seen as connected 47 | //TLE 48 | //64 / 66 test cases passed. 49 | class Solution { 50 | public: 51 | int gcd(int x, int y){ 52 | if(y == 0) return x; 53 | return gcd(y, x%y); 54 | } 55 | 56 | int encode(int& i, int& j, int& n){ 57 | return i*(n+1)+j; 58 | } 59 | 60 | vector areConnected(int n, int threshold, vector>& queries) { 61 | int m = queries.size(); 62 | vector ans(m, true); 63 | 64 | if(threshold == 0){ 65 | return ans; 66 | } 67 | 68 | vector> adjList(n+1); 69 | 70 | for(int i = 1; i <= n; ++i){ 71 | for(int j = i+1; j <= n; ++j){ 72 | if(gcd(i, j) > threshold){ 73 | adjList[i].insert(j); 74 | adjList[j].insert(i); 75 | } 76 | } 77 | } 78 | 79 | for(int i = 0; i < m; ++i){ 80 | vector& query = queries[i]; 81 | if(query[0] > query[1]) swap(query[0], query[1]); 82 | 83 | bool connected = false; 84 | 85 | queue q; 86 | vector visited(n+1, false); 87 | int cur; 88 | 89 | q.push(query[0]); 90 | visited[query[0]] = true; 91 | 92 | while(!q.empty()){ 93 | cur = q.front(); q.pop(); 94 | 95 | if(cur == query[1]){ 96 | connected = true; 97 | break; 98 | } 99 | 100 | for(const int& nei : adjList[cur]){ 101 | if(visited[nei]) continue; 102 | visited[nei] = true; 103 | q.push(nei); 104 | } 105 | } 106 | 107 | ans[i] = connected; 108 | } 109 | 110 | return ans; 111 | } 112 | }; 113 | 114 | //DSU 115 | //from hint 116 | //Runtime: 360 ms, faster than 57.32% of C++ online submissions for Graph Connectivity With Threshold. 117 | //Memory Usage: 65.4 MB, less than 6.13% of C++ online submissions for Graph Connectivity With Threshold. 118 | class DSU{ 119 | public: 120 | vector parent; 121 | 122 | DSU(int n){ 123 | parent = vector(n); 124 | iota(parent.begin(), parent.end(), 0); 125 | } 126 | 127 | int find(int x){ 128 | if(parent[x] == x) return x; 129 | return find(parent[x]); 130 | } 131 | 132 | void unite(int x, int y){ 133 | int px = find(x); 134 | int py = find(y); 135 | 136 | parent[py] = px; 137 | } 138 | }; 139 | 140 | class Solution { 141 | public: 142 | int gcd(int x, int y){ 143 | if(y == 0) return x; 144 | return gcd(y, x%y); 145 | } 146 | 147 | vector areConnected(int n, int threshold, vector>& queries) { 148 | int m = queries.size(); 149 | vector ans(m, true); 150 | 151 | if(threshold == 0){ 152 | return ans; 153 | } 154 | 155 | DSU dsu(n+1); 156 | 157 | for(int i = threshold+1; i <= n; ++i){ 158 | for(int j = i*2; j <= n; j += i){ 159 | //gcd(i, j) > threshold must hold! 160 | //if(gcd(i, j) > threshold){ 161 | dsu.unite(i, j); 162 | //} 163 | } 164 | } 165 | 166 | for(int i = 0; i < m; ++i){ 167 | vector& q = queries[i]; 168 | 169 | ans[i] = dsu.find(q[0]) == dsu.find(q[1]); 170 | } 171 | 172 | return ans; 173 | } 174 | }; 175 | -------------------------------------------------------------------------------- /18. 4Sum.md: -------------------------------------------------------------------------------- 1 | # 18. 4Sum 2 | 3 | [leetcode - 18. 4Sum](https://leetcode.com/problems/4sum/) 4 | 5 | Runtime: 28 ms, faster than 78.17% of C++ online submissions for 4Sum. 6 | 7 | Memory Usage: 8.5 MB, less than 100.00% of C++ online submissions for 4Sum. 8 | 9 | Just wrap 3Sum problem with one more outer loop. 10 | 11 | ```cpp 12 | class Solution { 13 | public: 14 | vector> fourSum(vector& nums, int target) { 15 | int N = nums.size(); 16 | vector> ans; 17 | 18 | sort(nums.begin(), nums.end()); 19 | 20 | for(int first = 0; first < N-3; first++){ 21 | if(first > 0 && nums[first] == nums[first-1]) continue; 22 | for(int second = first+1; second < N-2; second++){ 23 | if(second > first+1 && nums[second] == nums[second-1]) continue; 24 | int left = second+1, right = N-1; 25 | int curTarget = target - nums[first] - nums[second]; 26 | // cout << first << " " << second << " " << left << " " << right << endl; 27 | while(left < right){ 28 | if(nums[left] + nums[right] == curTarget){ 29 | ans.push_back({nums[first], nums[second], nums[left], nums[right]}); 30 | while(left < right && nums[left] == nums[left+1]) left++; 31 | while(left < right && nums[right] == nums[right-1]) right--; 32 | left++; 33 | right--; 34 | }else if(nums[left] + nums[right] < curTarget){ 35 | left++; 36 | }else{ 37 | right--; 38 | } 39 | } 40 | } 41 | } 42 | 43 | return ans; 44 | } 45 | }; 46 | ``` 47 | 48 | ## findNSum, recursive, optimized 49 | 50 | Runtime: 8 ms, faster than 98.35% of C++ online submissions for 4Sum. 51 | 52 | Memory Usage: 8.5 MB, less than 100.00% of C++ online submissions for 4Sum. 53 | 54 | ```cpp 55 | class Solution { 56 | public: 57 | void findNSum(int l, int r, int target, int N, vector& nums, vector& result, vector>& results){ 58 | if(r-l+1 < N || N < 2 || target < nums[l]*N || target > nums[r]*N){ 59 | //r-l+1 < N: range's width should >= N 60 | return; 61 | } 62 | if(N == 2){ 63 | while(l < r){ 64 | if(nums[l] + nums[r] == target){ 65 | result.push_back(nums[l]); 66 | result.push_back(nums[r]); 67 | // cout << "result: " << endl; 68 | // for(int i = 0; i < result.size(); i++){ 69 | // cout << result[i] << " "; 70 | // } 71 | // cout << endl; 72 | results.push_back(result); 73 | //need to revert the push_back above for later use! 74 | result.pop_back(); 75 | result.pop_back(); 76 | while(l < r && nums[l] == nums[l+1]){ 77 | l++; 78 | } 79 | l++; 80 | while(l < r && nums[r] == nums[r-1]){ 81 | r--; 82 | } 83 | r--; 84 | }else if(nums[l] + nums[r] < target){ 85 | l++; 86 | }else{ 87 | r--; 88 | } 89 | } 90 | }else{ 91 | //recursively reduce N 92 | //i <= r+1-N: because r-i+1 should >= N 93 | for(int i = l; i <= r+1-N; i++){ 94 | //skip duplicate nums[i] 95 | if(i == l || (i > l && nums[i] != nums[i-1])){ 96 | result.push_back(nums[i]); 97 | // cout << "i: " << i << ", nums[i]: " << nums[i] << ", [" << i+1 << ", " << r << "], target: " << target-nums[i] << " , N: " << N-1 << endl; 98 | findNSum(i+1, r, target-nums[i], N-1, nums, result, results); 99 | //need to revert the push_back above for later use! 100 | result.pop_back(); 101 | } 102 | } 103 | } 104 | }; 105 | 106 | vector> fourSum(vector& nums, int target) { 107 | sort(nums.begin(), nums.end()); 108 | // for(int i = 0; i < nums.size(); i++){ 109 | // cout << nums[i] << " "; 110 | // } 111 | // cout << endl; 112 | vector result; 113 | vector> results; 114 | findNSum(0, nums.size()-1, target, 4, nums, result, results); 115 | return results; 116 | } 117 | }; 118 | ``` 119 | -------------------------------------------------------------------------------- /213. House Robber II.cpp: -------------------------------------------------------------------------------- 1 | //DP 2 | //Runtime: 0 ms, faster than 100.00% of C++ online submissions for House Robber II. 3 | //Memory Usage: 6.5 MB, less than 100.00% of C++ online submissions for House Robber II. 4 | class Solution { 5 | public: 6 | int rob(vector& nums) { 7 | int N = nums.size(); 8 | if(N == 0) return 0; 9 | if(N <= 3) return *max_element(nums.begin(), nums.end()); 10 | vector dpA = nums, dpB = nums; 11 | 12 | //0~N-2 13 | for(int i = N-3; i >= 0; i--){ 14 | dpA[i] += max(((i+2 < N-1) ? dpA[i+2] : 0), 15 | ((i+3 < N-1) ? dpA[i+3] : 0)); 16 | } 17 | 18 | //1~N-1 19 | for(int i = N-3; i >= 1; i--){ 20 | dpB[i] += max(((i+2 < N) ? dpB[i+2] : 0), 21 | ((i+3 < N) ? dpB[i+3] : 0)); 22 | } 23 | 24 | return max(max(dpA[0], dpA[1]), 25 | max(dpB[1], dpB[2])); 26 | 27 | // return max(dpA[0], dpB[1]); 28 | } 29 | }; 30 | 31 | //DP, forward 32 | //Runtime: 0 ms, faster than 100.00% of C++ online submissions for House Robber II. 33 | //Memory Usage: 6.3 MB, less than 100.00% of C++ online submissions for House Robber II. 34 | class Solution { 35 | public: 36 | int rob(vector& nums) { 37 | int n = nums.size(); 38 | 39 | if(n == 0) return 0; 40 | if(n == 1) return nums[0]; 41 | if(n == 2) return max(nums[0], nums[1]); 42 | 43 | vector dpNoLast(n-1); 44 | vector dpNoFirst(n); //padding 0 first 45 | 46 | dpNoLast[0] = nums[0]; 47 | dpNoLast[1] = max(nums[0], nums[1]); 48 | 49 | for(int i = 2; i < dpNoLast.size(); i++){ 50 | //if we rob the ith house, we can add dp[i-2] because there is no alear 51 | dpNoLast[i] = max(dpNoLast[i-1], dpNoLast[i-2]+nums[i]); 52 | } 53 | 54 | //start from index 1 55 | //index 0 is just used for padding 56 | dpNoFirst[1] = nums[1]; 57 | dpNoFirst[2] = max(nums[1], nums[2]); 58 | 59 | for(int i = 3; i < dpNoFirst.size(); i++){ 60 | //if we rob the ith house, we can add dp[i-2] because there is no alear 61 | dpNoFirst[i] = max(dpNoFirst[i-1], dpNoFirst[i-2]+nums[i]); 62 | } 63 | 64 | return max(dpNoLast.back(), dpNoFirst.back()); 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /3 sum leetcode .cpp: -------------------------------------------------------------------------------- 1 | class Solution { 2 | public: 3 | vector> threeSum(vector& nums) { 4 | vector> ans; 5 | if(nums.size() < 3) return ans; 6 | sort(nums.begin(), nums.end()); 7 | 8 | for(int i = 0; i < nums.size()-2; ++i) 9 | { 10 | if(i != 0 && nums[i] == nums[i-1]) 11 | continue; 12 | 13 | int a = i + 1, b = nums.size() -1, t; 14 | while(a < b) 15 | { 16 | t = nums[i] + nums[a] + nums[b]; 17 | if(t > 0) 18 | b--; 19 | else if(t < 0) 20 | a++; 21 | else 22 | { 23 | ans.push_back({nums[i] , nums[a] , nums[b]}); 24 | a++; b--; 25 | while(nums[a] == nums[a-1] && a < b) 26 | a++; 27 | while(nums[b] == nums[b+1] && a < b) 28 | b--; 29 | } 30 | } 31 | } 32 | 33 | 34 | 35 | return ans; 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /30. Substring with Concatenation of All Words.cpp: -------------------------------------------------------------------------------- 1 | 2 | //Runtime: 544 ms, faster than 56.37% of C++ online submissions for Substring with Concatenation of All Words. 3 | //Memory Usage: 23.1 MB, less than 56.80% of C++ online submissions for Substring with Concatenation of All Words. 4 | class Solution { 5 | public: 6 | vector findSubstring(string s, vector& words) { 7 | int n = s.size(); 8 | int m = words.size(); 9 | 10 | vector ans; 11 | 12 | if(m == 0){ 13 | return ans; 14 | } 15 | 16 | int l = words[0].size(); 17 | 18 | unordered_map word2count; 19 | 20 | for(string& word : words){ 21 | ++word2count[word]; 22 | } 23 | 24 | //m*l: words' total length 25 | for(int i = 0; i + m*l - 1 < n; ++i){ 26 | int j; //index to "words" 27 | 28 | unordered_map word2visited; 29 | 30 | for(j = 0; j < m; ++j){ 31 | string subs(s.begin()+i+j*l, s.begin()+i+(j+1)*l); 32 | if(word2count.find(subs) == word2count.end()){ 33 | break; 34 | } 35 | ++word2visited[subs]; 36 | if(word2visited[subs] > word2count[subs]){ 37 | break; 38 | } 39 | } 40 | 41 | if(j == m){ 42 | ans.push_back(i); 43 | } 44 | } 45 | 46 | return ans; 47 | } 48 | }; 49 | -------------------------------------------------------------------------------- /40. Combination Sum II.cpp: -------------------------------------------------------------------------------- 1 | //Runtime: 12 ms, faster than 51.64% of C++ online submissions for Combination Sum II. 2 | //Memory Usage: 8.4 MB, less than 100.00% of C++ online submissions for Combination Sum II. 3 | class Solution { 4 | public: 5 | int tgt; 6 | 7 | void backtrack(vector>& combs, vector& comb, vector& candidates, vector& used, int start){ 8 | int cursum = accumulate(comb.begin(), comb.end(), 0); 9 | if(cursum > tgt){ 10 | }else if(cursum == tgt){ 11 | combs.push_back(comb); 12 | }else{ 13 | for(int i = start; i < candidates.size(); i++){ 14 | if(used[i]) continue; 15 | //if i-1 not used and we use i now, then i acts just as i-1 16 | if(i > 0 && candidates[i] == candidates[i-1] && !used[i-1]) continue; 17 | comb.push_back(candidates[i]); 18 | used[i] = true; 19 | backtrack(combs, comb, candidates, used, i+1); 20 | comb.pop_back(); 21 | used[i] = false; 22 | } 23 | } 24 | }; 25 | 26 | vector> combinationSum2(vector& candidates, int target) { 27 | int N = candidates.size(); 28 | vector> combs; 29 | vector comb; 30 | vector used(N, false); 31 | tgt = target; 32 | 33 | sort(candidates.begin(), candidates.end()); 34 | 35 | backtrack(combs, comb, candidates, used, 0); 36 | 37 | return combs; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /6. ZigZag Conversion.cpp: -------------------------------------------------------------------------------- 1 | //Runtime: 116 ms, faster than 8.32% of C++ online submissions for ZigZag Conversion. 2 | //Memory Usage: 40 MB, less than 9.36% of C++ online submissions for ZigZag Conversion. 3 | class Solution { 4 | public: 5 | string convert(string s, int numRows) { 6 | if(numRows == 1) return s; 7 | 8 | int n = s.size(); 9 | 10 | vector> zz(numRows, vector(n, ' ')); 11 | int r = 0, c = 0; 12 | //direction: false for down, true for up 13 | bool d = false; 14 | 15 | for(int i = 0; i < n; ++i){ 16 | // cout << "(" << r << ", " << c << ")" << endl; 17 | zz[r][c] = s[i]; 18 | if(!d){ 19 | ++r; 20 | }else{ 21 | --r; 22 | ++c; 23 | } 24 | 25 | if(r == 0 || r == numRows-1) 26 | d = !d; 27 | } 28 | 29 | string ans; 30 | 31 | for(int r = 0; r < numRows; ++r){ 32 | for(int c = 0; c < n; ++c){ 33 | if(zz[r][c] != ' '){ 34 | ans += zz[r][c]; 35 | if(ans.size() == n) break; 36 | } 37 | } 38 | if(ans.size() == n) break; 39 | } 40 | 41 | return ans; 42 | } 43 | }; 44 | 45 | //Approach 1: Sort by Row 46 | //Runtime: 12 ms, faster than 99.63% of C++ online submissions for ZigZag Conversion. 47 | //Memory Usage: 11 MB, less than 24.06% of C++ online submissions for ZigZag Conversion. 48 | //time: O(N), space: O(N) 49 | class Solution { 50 | public: 51 | string convert(string s, int numRows) { 52 | if(numRows == 1) return s; 53 | 54 | int n = s.size(); 55 | 56 | vector rows(min(numRows,n)); 57 | int r = 0; 58 | bool down = true; 59 | 60 | for(char c : s){ 61 | rows[r] += c; 62 | r += down ? 1 : -1; 63 | if(r == 0 || r == numRows-1) 64 | down = !down; 65 | } 66 | 67 | string ans; 68 | for(string& row : rows){ 69 | ans += row; 70 | } 71 | return ans; 72 | } 73 | }; 74 | 75 | //Approach 2: Visit by Row 76 | //Runtime: 12 ms, faster than 99.63% of C++ online submissions for ZigZag Conversion. 77 | //Memory Usage: 8.6 MB, less than 58.45% of C++ online submissions for ZigZag Conversion. 78 | //time: O(N), space: O(N) 79 | class Solution { 80 | public: 81 | string convert(string s, int numRows) { 82 | if(numRows == 1) return s; 83 | 84 | int n = s.size(); 85 | int cycleLen = 2*(numRows - 1); 86 | string ans; 87 | 88 | /* 89 | first row: k*cycleLen 90 | last row: k*cycleLen+numRows-1 91 | internal rows: k*cycleLen+i or (k+1)*cycleLen-i 92 | */ 93 | for(int i = 0; i < numRows; ++i){ 94 | for(int k = 0; i + k * cycleLen < n; ++k){ 95 | ans += s[i+k*cycleLen]; 96 | if(i != 0 && i != numRows-1 && (k+1)*cycleLen-i < n){ 97 | ans += s[(k+1)*cycleLen-i]; 98 | } 99 | } 100 | } 101 | 102 | return ans; 103 | } 104 | }; 105 | -------------------------------------------------------------------------------- /64. Minimum Path Sum.cpp: -------------------------------------------------------------------------------- 1 | //Backtracking 2 | //TLE 3 | //10 / 61 test cases passed. 4 | class Solution { 5 | public: 6 | int m, n; 7 | 8 | vector> dirs = { 9 | {1, 0}, 10 | {-1, 0}, 11 | {0, 1}, 12 | {0, -1} 13 | }; 14 | 15 | void backtrack(int& minSum, int curSum, int curI, int curJ, vector>& grid, vector>& visited){ 16 | if(curI == m-1 && curJ == n-1){ 17 | minSum = min(minSum, curSum); 18 | }else{ 19 | for(vector& dir : dirs){ 20 | int nextI = curI + dir[0]; 21 | int nextJ = curJ + dir[1]; 22 | if(nextI >= 0 && nextI < m && nextJ >= 0 && nextJ < n && !visited[nextI][nextJ]){ 23 | cout << nextI << " " << nextJ << " | "; 24 | visited[nextI][nextJ] = true; 25 | backtrack(minSum, curSum+grid[nextI][nextJ], nextI, nextJ, grid, visited); 26 | visited[nextI][nextJ] = false; 27 | } 28 | } 29 | } 30 | }; 31 | 32 | int minPathSum(vector>& grid) { 33 | this->m = grid.size(); 34 | if(this->m == 0) return 0; 35 | this->n = grid[0].size(); 36 | 37 | int minSum = INT_MAX; 38 | int curI = 0, curJ = 0; 39 | int curSum = grid[curI][curJ]; 40 | vector> visited(m, vector(n, false)); 41 | 42 | backtrack(minSum, curSum, curI, curJ, grid, visited); 43 | 44 | return minSum; 45 | } 46 | }; 47 | 48 | //DP 49 | //Note: You can only move either down or right at any point in time. 50 | //Runtime: 12 ms, faster than 23.69% of C++ online submissions for Minimum Path Sum. 51 | //Memory Usage: 8.5 MB, less than 100.00% of C++ online submissions for Minimum Path Sum. 52 | class Solution { 53 | public: 54 | int m, n; 55 | 56 | vector> dirs = { 57 | {-1, 0}, 58 | {0, -1} 59 | }; 60 | 61 | int minPathSum(vector>& grid) { 62 | this->m = grid.size(); 63 | if(this->m == 0) return 0; 64 | this->n = grid[0].size(); 65 | 66 | vector> dp = grid; 67 | 68 | for(int i = 0; i < m; i++){ 69 | for(int j = 0; j < n; j++){ 70 | int prevMin = INT_MAX; 71 | for(vector& dir : dirs){ 72 | int prevI = i + dir[0]; 73 | int prevJ = j + dir[1]; 74 | if(prevI >= 0 && prevJ >= 0){ 75 | prevMin = min(prevMin, dp[prevI][prevJ]); 76 | } 77 | } 78 | dp[i][j] += (prevMin == INT_MAX) ? 0 : prevMin; 79 | } 80 | } 81 | 82 | return dp[m-1][n-1]; 83 | } 84 | }; 85 | 86 | //DP without extra space! 87 | //https://leetcode.com/problems/minimum-path-sum/discuss/23457/C%2B%2B-DP 88 | //Runtime: 8 ms, faster than 84.87% of C++ online submissions for Minimum Path Sum. 89 | //Memory Usage: 8.1 MB, less than 100.00% of C++ online submissions for Minimum Path Sum. 90 | class Solution { 91 | public: 92 | int minPathSum(vector>& grid) { 93 | int m = grid.size(); 94 | if(m == 0) return 0; 95 | int n = grid[0].size(); 96 | 97 | for(int i = 0; i < m; i++){ 98 | for(int j = 0; j < n; j++){ 99 | //INT_MAX means invalid 100 | int top = (i-1>=0) ? grid[i-1][j] : INT_MAX; 101 | int left = (j-1>=0) ? grid[i][j-1] : INT_MAX; 102 | grid[i][j] += (min(top, left) == INT_MAX)?0:min(top, left); 103 | // cout << grid[i][j] << " "; 104 | } 105 | // cout << endl; 106 | } 107 | 108 | return grid[m-1][n-1]; 109 | } 110 | }; 111 | -------------------------------------------------------------------------------- /80. Remove Duplicates from Sorted Array II.cpp: -------------------------------------------------------------------------------- 1 | //Runtime: 12 ms, faster than 46.22% of C++ online submissions for Remove Duplicates from Sorted Array II. 2 | //Memory Usage: 11.3 MB, less than 13.64% of C++ online submissions for Remove Duplicates from Sorted Array II. 3 | class Solution { 4 | public: 5 | int removeDuplicates(vector& nums) { 6 | int n = nums.size(); 7 | int slow = 0, fast = 0; 8 | bool first; 9 | 10 | while(fast < n){ 11 | // cout << slow << " <- " << fast << endl; 12 | nums[slow] = nums[fast]; 13 | if(slow == 0 || nums[slow] != nums[slow-1]){ 14 | first = true; 15 | }else{ 16 | first = false; 17 | } 18 | 19 | if(first){ 20 | ++fast; 21 | }else{ 22 | while(fast < n && nums[fast] == nums[slow]){ 23 | ++fast; 24 | } 25 | } 26 | 27 | ++slow; 28 | } 29 | 30 | return slow; 31 | } 32 | }; 33 | 34 | //https://leetcode.com/problems/remove-duplicates-from-sorted-array-ii/discuss/27976/3-6-easy-lines-C%2B%2B-Java-Python-Ruby 35 | //Runtime: 4 ms, faster than 99.33% of C++ online submissions for Remove Duplicates from Sorted Array II. 36 | //Memory Usage: 11.3 MB, less than 14.37% of C++ online submissions for Remove Duplicates from Sorted Array II. 37 | class Solution { 38 | public: 39 | int removeDuplicates(vector& nums) { 40 | int n = nums.size(); 41 | int slow = 0; 42 | int k = 2; 43 | 44 | for(int fast = 0; fast < n; ++fast){ 45 | if(slow < k || nums[fast] != nums[slow-k]){ 46 | /* 47 | only update nums[slow] when 48 | there are < k nums[fast] in the new array 49 | */ 50 | nums[slow++] = nums[fast]; 51 | } 52 | } 53 | 54 | return slow; 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /89. Gray Code.cpp: -------------------------------------------------------------------------------- 1 | //backtracking 2 | //TLE 3 | //4 / 12 test cases passed. 4 | //testcase: 4 5 | class Solution { 6 | public: 7 | int n; 8 | vector ans; 9 | 10 | void backtrack(vector& perm, vector& used){ 11 | if(all_of(used.begin(), used.end(), [](const bool& b){return b;})){ 12 | bool valid = true; 13 | for(int i = 0; i+1 < n; ++i){ 14 | if(__builtin_popcount(perm[i]^perm[i+1]) != 1){ 15 | valid = false; 16 | break; 17 | } 18 | } 19 | if(valid){ 20 | ans = perm; 21 | } 22 | }else{ 23 | for(int i = 0; i < n; ++i){ 24 | if(used[i]) continue; 25 | perm.push_back(i); 26 | used[i] = true; 27 | backtrack(perm, used); 28 | if(!ans.empty()) return; 29 | perm.pop_back(); 30 | used[i] = false; 31 | } 32 | } 33 | } 34 | 35 | vector grayCode(int n) { 36 | this->n = 1< perm; 38 | vector used(1< ans; 51 | 52 | void backtrack(vector& perm, vector& used){ 53 | if(all_of(used.begin(), used.end(), [](const bool& b){return b;})){ 54 | bool valid = true; 55 | // for(int i = 0; i+1 < n; ++i){ 56 | // if(__builtin_popcount(perm[i]^perm[i+1]) != 1){ 57 | // valid = false; 58 | // break; 59 | // } 60 | // } 61 | if(valid){ 62 | ans = perm; 63 | } 64 | }else{ 65 | for(int i = 0; i < n; ++i){ 66 | if(used[i]) continue; 67 | if(!perm.empty() && 68 | __builtin_popcount(perm.back()^i) != 1) continue; 69 | perm.push_back(i); 70 | used[i] = true; 71 | backtrack(perm, used); 72 | if(!ans.empty()) return; 73 | perm.pop_back(); 74 | used[i] = false; 75 | } 76 | } 77 | } 78 | 79 | vector grayCode(int n) { 80 | this->n = 1< perm; 82 | vector used(1< ans; 96 | 97 | bool diffByOneBit(int a, int b){ 98 | int x = a^b; 99 | 100 | //x==0 <-> a==b 101 | //x&(x-1): remove x's last bit 102 | //((x&(x-1)) == 0): x has only one digit 103 | 104 | return (x!=0) && ((x&(x-1)) == 0); 105 | } 106 | 107 | void backtrack(vector& res, vector& used){ 108 | if(res.size() == p2n){ 109 | if(diffByOneBit(res[0], res.back())){ 110 | ans = res; 111 | } 112 | }else{ 113 | const int last = res.back(); 114 | for(int i = 0; i < n; ++i){ 115 | int cur = last ^ (1< grayCode(int n) { 128 | if(n == 0) return {0}; 129 | this->n = n; 130 | this->p2n = (1< res; 133 | vector used(this->p2n, false); 134 | 135 | res = {0}; 136 | used[0] = true; 137 | 138 | backtrack(res, used); 139 | 140 | return ans; 141 | } 142 | }; 143 | 144 | //iterative 145 | //https://leetcode.com/problems/gray-code/discuss/400651/Java-Solutions-with-Detailed-Comments-and-Explanations-(Backtracking-Prepending) 146 | //Runtime: 0 ms, faster than 100.00% of C++ online submissions for Gray Code. 147 | //Memory Usage: 6.7 MB, less than 48.06% of C++ online submissions for Gray Code. 148 | class Solution { 149 | public: 150 | vector grayCode(int n) { 151 | vector res = {0}; 152 | 153 | for(int i = 0; i < n; ++i){ 154 | /* 155 | in ith iteration, 156 | we change res of size 1<= 0; --j){ 170 | res.push_back(prependVal + res[j]); 171 | } 172 | /* 173 | the numbers in the later half all diff by one bit, 174 | because they are created from the old res 175 | 176 | res[oldSize-1] and res[oldSize] diff by one bit 177 | (the most significant bit) 178 | 179 | res[newSize-1] and res[0] diff by one bit 180 | (the most significant bit) 181 | */ 182 | } 183 | 184 | return res; 185 | } 186 | }; 187 | -------------------------------------------------------------------------------- /99. Recover Binary Search Tree.cpp: -------------------------------------------------------------------------------- 1 | //Runtime: 56 ms, faster than 20.38% of C++ online submissions for Recover Binary Search Tree. 2 | //Memory Usage: 55.2 MB, less than 5.06% of C++ online submissions for Recover Binary Search Tree. 3 | //time: O(N), space: O(N) 4 | /** 5 | * Definition for a binary tree node. 6 | * struct TreeNode { 7 | * int val; 8 | * TreeNode *left; 9 | * TreeNode *right; 10 | * TreeNode() : val(0), left(nullptr), right(nullptr) {} 11 | * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 12 | * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 13 | * }; 14 | */ 15 | class Solution { 16 | public: 17 | vector nodes; 18 | vector vals; 19 | 20 | void inorder(TreeNode* node){ 21 | if(!node) return; 22 | inorder(node->left); 23 | nodes.push_back(node); 24 | vals.push_back(node->val); 25 | inorder(node->right); 26 | } 27 | 28 | void inorder_revise(TreeNode* node, int& order, vector& indices_to_revise, vector& vals_to_revise){ 29 | if(!node) return; 30 | inorder_revise(node->left, order, indices_to_revise, vals_to_revise); 31 | auto it = find(indices_to_revise.begin(), indices_to_revise.end(), order); 32 | if(it != indices_to_revise.end()){ 33 | // cout << "revise " << node->val << " to " << vals_to_revise[it-indices_to_revise.begin()] << endl; 34 | node->val = vals_to_revise[it-indices_to_revise.begin()]; 35 | } 36 | ++order; 37 | inorder_revise(node->right, order, indices_to_revise, vals_to_revise); 38 | } 39 | 40 | void recoverTree(TreeNode* root) { 41 | inorder(root); 42 | 43 | //the values in right order 44 | sort(vals.begin(), vals.end()); 45 | 46 | vector indices_to_revise; 47 | vector vals_to_revise; 48 | for(int i = 0; i < nodes.size(); ++i){ 49 | //the node's value isn't the value it should be 50 | if(nodes[i]->val != vals[i]){ 51 | indices_to_revise.push_back(i); 52 | vals_to_revise.push_back(vals[i]); 53 | } 54 | } 55 | 56 | int order = 0; 57 | inorder_revise(root, order, indices_to_revise, vals_to_revise); 58 | } 59 | }; 60 | 61 | //Morris Traversal, threaded binary tree 62 | //https://leetcode.com/problems/recover-binary-search-tree/discuss/32559/Detail-Explain-about-How-Morris-Traversal-Finds-two-Incorrect-Pointer 63 | //Runtime: 48 ms, faster than 33.92% of C++ online submissions for Recover Binary Search Tree. 64 | //Memory Usage: 53.7 MB, less than 33.72% of C++ online submissions for Recover Binary Search Tree. 65 | //time: O(N), space: O(1) 66 | /** 67 | * Definition for a binary tree node. 68 | * struct TreeNode { 69 | * int val; 70 | * TreeNode *left; 71 | * TreeNode *right; 72 | * TreeNode() : val(0), left(nullptr), right(nullptr) {} 73 | * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 74 | * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 75 | * }; 76 | */ 77 | class Solution { 78 | public: 79 | TreeNode *cur, *prev; 80 | TreeNode *first, *second; 81 | 82 | void doWork(){ 83 | //do work related to this problem 84 | if(prev != nullptr && prev->val > cur->val){ 85 | if(first == nullptr){ 86 | first = prev; 87 | // cout << "first: " << prev->val << endl; 88 | } 89 | /* 90 | also set second when first == nullptr, 91 | this is so deal with the case that 92 | first and second may be consecutive 93 | */ 94 | second = cur; 95 | // cout << "second: " << cur->val << endl; 96 | } 97 | //maintain the "prev" pointer 98 | prev = cur; 99 | }; 100 | 101 | void recoverTree(TreeNode* root) { 102 | cur = root; 103 | prev = nullptr; 104 | first = second = nullptr; 105 | 106 | //the framework is Morris traversal 107 | while(cur){ 108 | if(cur->left){ 109 | //find its predecessor in its left subtree 110 | TreeNode* pred = cur->left; 111 | while(pred->right != nullptr && pred->right != cur){ 112 | pred = pred->right; 113 | } 114 | 115 | if(pred->right == nullptr){ 116 | /* 117 | connect the predecessor's right to cur, 118 | so we can come back to cur later 119 | */ 120 | pred->right = cur; 121 | /* 122 | now that it is ensured that we can go back to cur later, 123 | we can go to its left subtree safely 124 | */ 125 | cur = cur->left; 126 | }else{ 127 | //here pred->right == cur 128 | 129 | doWork(); 130 | // cout << cur->val << endl; 131 | 132 | pred->right = nullptr; 133 | /* 134 | we have visit cur's left subtree and cur itself, 135 | so go to its right subtree 136 | */ 137 | cur = cur->right; 138 | } 139 | }else{ 140 | doWork(); 141 | // cout << cur->val << endl; 142 | cur = cur->right; 143 | } 144 | } 145 | 146 | if(first != nullptr && second != nullptr){ 147 | swap(first->val, second->val); 148 | } 149 | } 150 | }; 151 | -------------------------------------------------------------------------------- /Check If a String Is a Valid Sequence from Root to Leaves Path in a Binary Tree.cpp: -------------------------------------------------------------------------------- 1 | //backtracking 2 | //Runtime: 92 ms 3 | //Memory Usage: 49 MB 4 | /** 5 | * Definition for a binary tree node. 6 | * struct TreeNode { 7 | * int val; 8 | * TreeNode *left; 9 | * TreeNode *right; 10 | * TreeNode() : val(0), left(nullptr), right(nullptr) {} 11 | * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 12 | * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 13 | * }; 14 | */ 15 | enum class COMP_RES{ 16 | SAME, 17 | //used when path is not complete but path is equal to former path of arr 18 | NOT_DIFF, 19 | DIFF 20 | }; 21 | 22 | class Solution { 23 | public: 24 | vector arr; 25 | 26 | COMP_RES isValid(vector& path){ 27 | //check if path equals to path 28 | //this can be used by incomplete path 29 | if(path.size() > arr.size()) return COMP_RES::DIFF; 30 | for(int i = 0; i < path.size(); i++){ 31 | if(arr[i] != path[i]) return COMP_RES::DIFF; 32 | } 33 | //return NOT_DIFF when path is equal to former part of arr 34 | return (path.size() == arr.size()) ? COMP_RES::SAME : COMP_RES::NOT_DIFF; 35 | } 36 | 37 | COMP_RES backtrack(TreeNode* node, vector& path){ 38 | if(!node->left && !node->right){ 39 | //leaf node 40 | return isValid(path); 41 | }else if(path.size() >= arr.size()){ 42 | //early stopping 43 | return COMP_RES::DIFF; 44 | } 45 | 46 | COMP_RES res = isValid(path); 47 | if(res == COMP_RES::DIFF || res == COMP_RES::SAME) return res; 48 | //now res is COMP_RES::NOT_DIFF 49 | //if former part of path is equal to that of arr 50 | //continue to search and append path 51 | res = COMP_RES::DIFF; 52 | if(node->left){ 53 | path.push_back(node->left->val); 54 | res = backtrack(node->left, path); 55 | if(res == COMP_RES::SAME) return COMP_RES::SAME; 56 | path.pop_back(); 57 | } 58 | if(node->right){ 59 | path.push_back(node->right->val); 60 | res = backtrack(node->right, path); 61 | if(res == COMP_RES::SAME) return COMP_RES::SAME; 62 | path.pop_back(); 63 | } 64 | 65 | //neither of its children match arr 66 | return COMP_RES::DIFF; 67 | }; 68 | 69 | bool isValidSequence(TreeNode* root, vector& arr) { 70 | if(!root) return false; 71 | this->arr = arr; 72 | vector path = {root->val}; 73 | return backtrack(root, path) == COMP_RES::SAME; 74 | } 75 | }; 76 | 77 | //recursive 78 | //https://leetcode.com/explore/featured/card/30-day-leetcoding-challenge/532/week-5/3315/discuss/604349/CPP-SIMPLE-EASY-DFS-SOLUTION-WITH-COMMENTS 79 | //Runtime: 96 ms 80 | //Memory Usage: 48.9 MB 81 | /** 82 | * Definition for a binary tree node. 83 | * struct TreeNode { 84 | * int val; 85 | * TreeNode *left; 86 | * TreeNode *right; 87 | * TreeNode() : val(0), left(nullptr), right(nullptr) {} 88 | * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 89 | * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 90 | * }; 91 | */ 92 | class Solution { 93 | public: 94 | bool isValidSequence(TreeNode* root, vector& arr, int i = 0) { 95 | if(i <= arr.size() && !root){ 96 | return false; 97 | } 98 | //stop when i is equal to arr.size()-1 99 | if(i == arr.size()-1){ 100 | //check if its leaf 101 | return root->val == arr[i] && !root->left && !root->right; 102 | } 103 | //continue to increase i 104 | return root->val == arr[i] && (isValidSequence(root->left, arr, i+1) || isValidSequence(root->right, arr, i+1)); 105 | } 106 | }; 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hacktoberoct-2k22 --------------------------------------------------------------------------------