1. 双指针法(定长,变长);
滑动窗口:3. 无重复字符的最长子串
1 //滑动窗口 + 二分 2 3 class Solution { 4 public: 5 bool check(int len, string s) { 6 vector<int> cnt(256, 0); 7 int k = 0; 8 for(int i = 0; s[i]; ++i) { 9 if(0 == cnt[s[i]]) k += 1; 10 cnt[s[i]] += 1; 11 12 if(i >= len) { 13 if(0 == (cnt[s[i - len]] -= 1)) k -= 1; 14 } 15 if(k == len) return true; 16 } 17 return false; 18 } 19 int lengthOfLongestSubstring(string s) { 20 int l = 0, r = s.size(); 21 while(l < r) { //10二分 22 int mid = (l + r + 1) >> 1; 23 if(check(mid, s)) l = mid; 24 else r = mid - 1; 25 } 26 return l; 27 } 28 };
1 class Solution { 2 public: 3 string minWindow(string s, string t) { 4 string ans = ""; 5 if(t.size() > s.size()) return ans; 6 unordered_map<char, int> cnt; 7 for(int i = 0; t[i]; ++i) cnt[t[i]] -= 1; 8 9 int sum = cnt.size(), pre = -1; 10 11 for(int i = 0; s[i]; ++i) { 12 if(cnt.find(s[i]) == cnt.end()) continue; 13 cnt[s[i]] += 1; 14 if(0 == cnt[s[i]]) sum -= 1; 15 if(sum) continue; 16 for(pre += 1; pre < i; ++pre) { 17 if(cnt.find(s[pre]) == cnt.end()) continue; 18 if(0 == cnt[s[pre]]) sum += 1; 19 cnt[s[pre]] -= 1; 20 if(sum) break; 21 } 22 if(!ans.size() || (i - pre + 1) < ans.size()) ans = s.substr(pre, (i - pre + 1)); 23 } 24 return ans; 25 } 26 };
1574. 删除最短的子数组使剩余数组有序 : 注意边界条件的处理;
1 class Solution { 2 public: 3 int findLengthOfShortestSubarray(vector<int>& arr) { 4 int ans = INT_MAX, len = arr.size(), index = len - 1; 5 while(index && arr[index] >= arr[index - 1]) index -= 1; 6 if(!index) return 0; 7 8 ans = index; 9 10 for(int i = 0, j = index; i < len; ++i) { 11 if(i && arr[i - 1] > arr[i]) break; 12 //最终的条件考虑情况更完整,虽然j = i 只有在整个数组都是非递减的状态下存在(已经在上面特判了); 13 // while(j < len && arr[j] < arr[i]) j++; 14 while(j <= i || (j < len && arr[j] < arr[i])) j++; 15 ans = min(ans, j - i - 1); 16 } 17 return ans; 18 } 19 };
2. 数组记忆化,空间换时间;
1 //纯粹的DFS 2 class Solution { 3 public: 4 int dfs(vector<int> &nums, int i, int target) { 5 if(i == nums.size()) return 0 == target; 6 int ret = 0; 7 ret += dfs(nums, i + 1, target - nums[i]); 8 ret += dfs(nums, i + 1, target + nums[i]); 9 return ret; 10 } 11 12 int findTargetSumWays(vector<int>& nums, int target) { 13 return dfs(nums, 0, target); 14 } 15 }; 16 17 18 //记忆化搜索 19 class Solution { 20 public: 21 typedef pair<int,int> PII; 22 struct CMP { 23 int operator() (const PII& a) const { 24 return a.first ^ a.second; 25 } 26 }; 27 unordered_map<PII,int, CMP> record; 28 29 int dfs(int i, vector<int> &nums, int target) { 30 if(i == nums.size()) return 0 == target; 31 32 if(record.find(PII(i, target)) != record.end()) return record[PII(i, target)]; 33 int cnt = 0; 34 cnt += dfs(i + 1, nums, target - nums[i]); 35 cnt += dfs(i + 1, nums, target + nums[i]); 36 record[PII(i, target)] = cnt; 37 return cnt; 38 } 39 40 int findTargetSumWays(vector<int>& nums, int target) { 41 return dfs(0, nums, target); 42 } 43 };
2.1 搜索减枝: 但当状态定义较为复杂时候,会导致记忆化数值复杂度 及 重复性降低,这时候就要考虑搜索减枝的方式减少DFS 的深度;
473. 火柴拼正方形 : 利用减枝优化查找效率,但是减枝的具体方法都要基于具体情况进行处理;
1 //没有任何减枝优化,很容易超时 2 class Solution { 3 public: 4 bool dfs(vector<int> &matchsticks, int index, array<int, 4> &sticks) { 5 if(index == matchsticks.size()) return true; 6 7 for(int i = 0; i < 4; ++i) { 8 int temp = sticks[i] - matchsticks[index]; 9 if(temp < 0) continue; 10 sticks[i] = temp; 11 if(dfs(matchsticks, index + 1, sticks)) return true; 12 sticks[i] = temp + matchsticks[index]; 13 } 14 15 return false; 16 } 17 18 bool makesquare(vector<int>& matchsticks) { 19 int total = 0; 20 for(auto &x : matchsticks) total += x; 21 if(total % 4) return false; 22 total /= 4; 23 24 array<int, 4> sticks = {total, total, total, total}; 25 return dfs(matchsticks, 0, sticks); 26 27 } 28 }; 29 30 31 32 //rev1:有效利用搜索减枝改善查找效率 33 class Solution { 34 public: 35 bool dfs(vector<int> &ms, vector<int> &arr, int ind) { 36 //状态定义比较复杂,增加记忆化数据的复杂度,重复性降低,使用搜索减枝的情况会更好; 37 //1.最长的火柴比当前边长要长,所以这个树节点的其中一个分支不可能找到结果了 38 //2. 当前边基于当前火柴处理后,还有剩余的话,如果比 最短的火柴还要短,那也不可能拼出来了 39 40 if(ind < 0) return true; 41 for(int i = 0; i < 4; i++) { 42 if(arr[i] < ms[ind]) continue; 43 if(arr[i] == ms[ind] || arr[i] >= ms[ind]+ ms[0]) { 44 arr[i] -= ms[ind]; 45 if(dfs(ms, arr, ind - 1)) return true; 46 arr[i] += ms[ind]; 47 } 48 } 49 return false; 50 } 51 52 bool makesquare(vector<int>& matchsticks) { 53 int all_sum = 0; 54 for(auto x : matchsticks) all_sum += x; 55 if(all_sum % 4) return false; 56 vector<int> arr(4, all_sum / 4); 57 //一个很好想法,排序以后从大到小查找,减少不必要继续查找; 58 sort(matchsticks.begin(), matchsticks.end()); 59 60 return dfs(matchsticks, arr, matchsticks.size() - 1); 61 } 62 };
3. 前缀节点(哨兵节点):
虚拟空节点: 实现AVL 树中,利用虚拟空节点,优化代码;
1 #include <iostream> 2 using namespace std; 3 4 #define NIL (&Node::__NIL) 5 6 struct Node { 7 Node(int k = 0, int h = 0, Node *l = NIL, Node *r = NIL) 8 : key(k), h(h), left(l), right(r) {} 9 int key, h; 10 Node *left, *right; 11 static Node __NIL; //这里只是声明 12 }; 13 14 Node Node::__NIL; //对于静态变量的使用,这里才是定义 15 16 Node *getNewNode(int key) { 17 return new Node(key,1); 18 } 19 20 void clear(Node *root) { 21 if(root == NIL) return; 22 clear(root->left); 23 clear(root->right); 24 cout << "del root key: " << root->key << endl; 25 delete root; 26 return; 27 } 28 29 void update_height(Node *root) { 30 //NIL if(root->left = nullptr && root->right == nullptr) return; 31 //NIL if(root->left == nullptr) root->h = root->right->h + 1; 32 //NIL else if(root->right == nullptr) root->h = root->left->h + 1; 33 root->h = max(root->left->h, root->right->h) + 1; 34 return; 35 } 36 37 Node *left_rotate(Node *root){ 38 Node *new_root = root->right; 39 root->right = new_root->left; 40 new_root->left = root; 41 update_height(root); 42 update_height(new_root); 43 return new_root; 44 } 45 46 Node *right_rotate(Node *root){ 47 Node *new_root = root->left; 48 root->left= new_root->right; 49 new_root->right= root; 50 update_height(root); 51 update_height(new_root); 52 return new_root; 53 } 54 55 const char *Type[5] = {"", "LL", "RR", "LR", "RL"}; 56 57 Node *maintain(Node *root) { 58 if(abs(root->left->h - root->right->h) < 2) return root; 59 int type = 0; 60 if(root->left->h > root->right->h) { 61 if(root->left->right->h > root->left->left->h) { 62 //lr 63 printf("%d : left rotate\n", root->left->key); 64 type = 3; 65 root->left = left_rotate(root->left); 66 } 67 //ll 68 if(!type) type = 1; 69 printf("%d : right rotate\n", root->key); 70 root = right_rotate(root); 71 }else { 72 if(root->right->left->h > root->right->right->h) { 73 //rl 74 type = 4; 75 printf("%d : right rotate\n", root->right->key); 76 root->right = right_rotate(root->right); 77 } 78 //rr 79 printf("%d : left rotate\n", root->key); 80 if(!type) type = 2; 81 root = left_rotate(root); 82 } 83 printf("maintian the type = %s;\n", Type[type]); 84 return root; 85 } 86 87 Node *insert(Node *root, int key) { 88 printf("insert %d\n", key); 89 if(root == NIL) return getNewNode(key); 90 if(root->key == key) return root; 91 92 if(key < root->key) root->left = insert(root->left, key); 93 else root->right = insert(root->right, key); 94 update_height(root); 95 return maintain(root); 96 } 97 98 Node* predeccesor(Node *root) { 99 Node *temp = root->left; 100 while(temp->right != NIL) temp = temp->right; 101 return temp; 102 } 103 104 Node *erase(Node *root, int key){ 105 printf("erase the %d\n", key); 106 if(root == NIL) return root; 107 if(key < root->key) root->left = erase(root->left, key); 108 else if(key > root->key) root->right = erase(root->right, key); 109 else { 110 //删除节点数为0 或 1 的节点 111 if(root->left == NIL || root->right == NIL) { 112 Node *temp = root->left == NIL ? root->right : root->left; 113 delete root; 114 return temp; 115 }else { //删除节点数为2 , 这里使用左子树最右边的值替补被删除的数(也可以通过右子树最左边的值替补被删除的值) 116 Node *temp = predeccesor(root); 117 root->key = temp->key; 118 root->left = erase(root->left, temp->key); 119 } 120 } 121 update_height(root); 122 return maintain(root); 123 } 124 125 void print(Node *root) { 126 printf("(%d[%d] | %d, %d)\n", root->key, root->h, root->left->key, root->right->key); 127 return; 128 } 129 130 void output(Node *root) { 131 if(root == NIL) return; 132 print(root); 133 output(root->left); 134 output(root->right); 135 return; 136 } int main() 137 { 138 int op, val; 139 Node *root = NIL; 140 while(cin >> op >> val) { 141 cout << endl << "====AVL tree print ====" << endl; 142 switch (op) { 143 case 0 : root = insert(root, val); break; 144 case 1 : root = erase(root, val); break; 145 } 146 output(root); 147 cout << endl << "====tree print end====" << endl; 148 } 149 150 clear(root); 151 152 return 0; 153 }
KMP/SUNDAY 算法中也有类似的前缀节点,作为万能匹配位,解决边界条件;
4. 前缀和(差分)数组, 树状数组(FenwickTree)
a). 区间加/减操作,天生适用差分数组,只要做两次单点操作; 1109. 航班预订统计
1 class Solution { 2 public: 3 vector<int> corpFlightBookings(vector<vector<int>>& bookings, int n) { 4 vector<int> arr(n, 0); 5 for(auto &x : bookings) { 6 arr[x[0] - 1] += x[2]; 7 if(x[1] == n) continue; 8 arr[x[1]] -= x[2]; 9 } 10 for(int i = 1; i < n; ++i) arr[i] += arr[i - 1]; 11 return arr; 12 } 13 };
5. 递归 :(欧拉公式,辗转相除法)
a: 数学归纳法 --> 结构归纳法: 证明程序的正确性, 而不是通过不断展开;
step1: k0 已知; // 确定边界条件;
step 2: 假设 ki 可以推导出 ki+1; // 赋予递归函数一个明确定义,确定相互关系(等式)
step3: 所以通过k0 可以 一直推导出ki;// 实现递归过程代码;
递归问题中,难点在于如何给予递归函数一个更好的定义,下面题目,两个递归方式实现:
: 个人感觉第一种方案,通过两个函数的递归更加优美。1 /** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 int getPath(TreeNode *r, int sum) { 13 if(!r) return 0; 14 int left = sum - r->val; 15 return (sum == r->val) + getPath(r->left,left) + getPath(r->right, left); 16 } 17 18 int pathSum(TreeNode* root, int sum) { 19 if(!root) return 0; 20 return getPath(root, sum) + 21 pathSum(root->left, sum) + 22 pathSum(root->right, sum); 23 } 24 };
1 /** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 int __pathSum(TreeNode *r, int depth, int sum) { 13 if(!r) return 0; 14 int left = sum - r->val, 15 ret = (left == 0); 16 17 if(0 == depth) { 18 ret += __pathSum(r->left, 0, sum); 19 ret += __pathSum(r->right, 0, sum); 20 } 21 22 ret += __pathSum(r->left, depth + 1, left); 23 ret += __pathSum(r->right, depth + 1, left); 24 25 return ret; 26 } 27 28 int pathSum(TreeNode* root, int sum) { 29 return __pathSum(root, 0, sum); 30 } 31 };
6. 方向数组 : 通常在查找中通过方向数组 定义 下一个状态 可能存在的变化范围;
7. 公式化简:leetcode1829
1 class Solution { 2 public: 3 vector<int> getMaximumXor(vector<int>& nums, int maximumBit) { 4 int max_val = (1 << maximumBit) - 1, len = nums.size(), preXor = 0; 5 vector<int> ret(len, 0); 6 for(int i = 0; i < len; ++i) { 7 preXor ^= nums[i]; 8 ret[len - 1 - i] = preXor ^ max_val; 9 } 10 11 return ret; 12 } 13 };
8. 枚举法:求质数,
9. 分治算法:(典型就是 mergeSort, 将一个大问题拆分,分别计算出各个部分的结果,然后再计算横跨不同部分的结果);
10. C++ 中 string to int 有 stoi, 那么int to string 没有函数,可以使用 stringstream 方式(也可以自己写转换函数);
1 #include <iostream> 2 #include <sstream> 3 4 using namespace std; 5 int main() 6 { 7 int val = 10; 8 string s = ""; 9 stringstream ss; 10 ss << val; 11 ss >> s; 12 printf("%s\n", s.c_str()); 13 return 0; 14 } 15 gu
11. 分块处理问题(快速幂计算, leetcode372; leetcode60)
快速幂:
1 #define MOD_NUM ((long long) 1337) 2 long long _pow(long long base, long long n) { 3 long long ret = 1; 4 while(n) { 5 if(n & 1) ret = (ret * base) % MOD_NUM; 6 base = base * base % MOD_NUM; 7 n >>= 1; 8 } 9 return ret; 10 }
12. 摩尔投票法:(leetcode229)
本质是将根据数量,进行相互抵消,直到最后剩下的值,再进行判断;
抵消有两种方式实现,一种是自身 + 1, 另一种是其他所有的都 -1,这两种方式都等价于抵消操作;
求 > 1 / n,就将数组分成 n 份。
1 class Solution { 2 public: 3 vector<int> majorityElement(vector<int>& nums) { 4 #define k 2 5 int size = nums.size(); 6 7 int cnt[k] = {0}, ans[k] ={0}; 8 for(int i = 0; i < size; ++i) { 9 int flag = false; 10 for(int ind = 0; ind < k; ++ind) { 11 if(nums[i] == ans[ind]) { 12 cnt[ind] += 1; 13 flag = true; 14 break; 15 } 16 } 17 if(flag) continue; 18 19 for(int ind = 0; ind < k; ++ind) { 20 if(cnt[ind]) continue; 21 ans[ind] = nums[i]; 22 cnt[ind] += 1; 23 flag = true; 24 break; 25 } 26 if(flag) continue; 27 28 for(int ind = 0; ind < k; ++ind) cnt[ind] -= 1; 29 } 30 31 for(int ind = 0; ind < k; ++ind) cnt[ind] = 0; 32 for(int i = 0; i < size; ++i) { 33 for(int ind = 0; ind < k; ++ind) { 34 if(nums[i] != ans[ind]) continue; 35 cnt[ind] += 1; 36 break; 37 } 38 } 39 vector<int> ret; 40 //注意这里要求的是超过多少,这里以3为例; 41 for(int ind = 0, I = size /(k + 1); ind < k; ++ind) if( cnt[ind] > I) ret.push_back(ans[ind]); 42 return ret; 43 } 44 };
13. 复杂问题简单化(扩展欧几里德算法(贝祖等式):
1 int ex_gcd(int a, int b, int &x, int &y){ 2 if(!b) { 3 x = 1, y = 0; 4 cout << a << " * " << x << " + " << b << " * " << y << " = "; 5 return a; 6 } 7 8 int ret = ex_gcd(b, a % b, y, x); 9 y -= a / b * x; 10 cout << a << " * " << x << " + " << b << " * " << y << " = "; 11 return ret; 12 } 13 14 int main() 15 { 16 int a, b, x, y, ret = 0; 17 18 while(cin >> a >> b) { 19 ret = ex_gcd(a, b, x, y); 20 cout << ret << endl; 21 } 22 return 0; 23 } 24 25 ex_gcd
14. 数学推导结果(均匀随机数生成 leetcode470)
1 class Solution { 2 public: 3 int rand10() { 4 while(1){ 5 int ans = (rand7() - 1) * 7 + rand7(); 6 if(ans <= 40) return ans % 10 + 1; 7 8 //rand 9 int a = ans - 40; //1 ~ 9 10 ans = (a - 1) * 7 + rand7(); // 63 11 if(ans <= 60) return ans % 10 + 1; 12 13 a = ans - 60; //1 ~ 3 14 ans = (a - 1) * 7 + rand7(); //21 15 if(ans <= 20) return ans % 10 + 1; 16 } 17 return 0; 18 } 19 };
4. 寻找两个正序数组的中位数 : 利用数学建模; 保证既满足条件,也不会超出范围;
对于可能两个数组需要分别选择指定数量数值,但是有可能指定数量大于 数组总数 情况下,如何取值做法; 很好的例子
int cnt1 = min(k / 2, (int)nums1.size() - i), cnt2 = min(k - cnt1, (int)nums2.size() - j);
cnt1 = k - cnt2;
任何 涉及到位置变化的 计算部分,都要考虑 越界可能;
k = 0时,k / 2 = 0, i + k / 2 - 1存在越界可能
1 class Solution { 2 public: 3 4 double findKnum(vector<int> &nums1, vector<int> &nums2, int i, int j, int k) { 5 6 //先判断是否存在越界情况; 7 if(i == nums1.size()) return nums2[j + k - 1]; 8 if(j == nums2.size()) return nums1[i + k - 1]; 9 //在判断特殊情况, k = 0时,k / 2 = 0, i + k / 2 - 1存在越界可能; 10 //必须要特判 11 if(k == 1) return (nums1[i] > nums2[j] ? nums2[j] : nums1[i]); 12 //对于可能存在越界情况下,如何取值做法; 很好的例子 13 int cnt1 = min(k / 2, (int)nums1.size() - i), 14 cnt2 = min(k - cnt1, (int)nums2.size() - j); 15 cnt1 = k - cnt2; 16 17 int index1 = i + cnt1 - 1, index2 = j + cnt2 - 1; 18 if(nums1[index1] == nums2[index2]) return nums1[index1]; 19 else if(nums1[index1] < nums2[index2]) return findKnum(nums1, nums2, index1 + 1, j, k - cnt1); 20 return findKnum(nums1, nums2, i, index2 + 1, k - cnt2); 21 } 22 23 double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) { 24 // int I1 = nums1.size(), I2 = nums2.size(); 25 // int mid = (I1 + I2 - 1) >> 1; 26 // //set STL 使用,然后寻找中位数 27 // multiset<int> arr; 28 // for(int i = 0; i < I1; i++) arr.insert(nums1[i]); 29 // for(int i = 0; i < I2; i++) arr.insert(nums2[i]); 30 // int temp = mid; 31 // multiset<int>::iterator iter = arr.begin(); 32 // while(temp--) iter++; 33 // 34 // if(mid * 2 == arr.size() - 1) return *(iter); 35 // else return 1.0 * (*iter + *(++iter)) / 2; 36 // 37 //二分方式,找到第k个数据 38 int a = nums1.size(), b = nums2.size(), k = (a + b + 1) / 2; 39 double ret = findKnum(nums1, nums2, 0, 0, k); 40 if((a + b) % 2) return ret; 41 int ret1 = findKnum(nums1, nums2, 0, 0, k + 1); 42 return (ret + ret1) / 2.0; 43 } 44 };
462. 最少移动次数使数组元素相等 II : 转换成数学公式: ans = ∑ |xi - x| 求最小值;
1 class Solution { 2 public: 3 int minMoves2(vector<int>& nums) { 4 sort(nums.begin(), nums.end()); 5 int n = nums.size(), mid = 0; 6 mid = nums[n / 2]; 7 8 int ret = 0; 9 for(auto &x : nums) ret += abs(x - mid); 10 return ret; 11 12 } 13 };
15. 对于各种数据结构类型使用的考虑方式:
a. 判断每次提取数据类型(最近的(stack) , 最早的(deque),最大的(priority_queue), 特定的下标(map/hash), 更改相当部分数据(前缀和,树状数组(fenwickTree));
b. 连同性问题: 并查集
c. 字符串相关: trie/ 多叉树
16. 需要返回多个查找结果时候,可以将不同查找值对应成为不同bit 位(面试题 04.08. 首个共同祖先)
1 /** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public: 12 int checkNode(TreeNode *root, TreeNode *p, TreeNode *q, TreeNode* &ret) { //return valu : 1 :find p; 2 find q;, 3 find both; 13 if(!root) return 0; 14 int l = checkNode(root->left, p, q, ret), 15 r = checkNode(root->right, p, q, ret); 16 if(l == 3 || r == 3) return 3; 17 int cur = (l | r); 18 if(root == p) cur |= 1; 19 if(root == q) cur |= 2; 20 if(cur == 3) ret = root; 21 return cur; 22 } 23 24 TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { 25 TreeNode* ret = nullptr; 26 checkNode(root, p, q, ret); 27 return ret; 28 } 29 };
17 . 利用倒序 求某个元素后面满足某种性质的元素;
正序要随着后面元素压入导致需要不断更新前面的结果;
456. 132 模式 : 求取某个元素后面小于它的最大值情况;(当然这题里面实际求的是它 与 大于它元素/结尾 之间的最接近它的最小值);
1 class Solution { 2 public: 3 bool find132pattern(vector<int>& nums) { 4 //132 pattern : 对于3 来讲,要求得前面小于它的最小值, 后面小于它的最大值; 然后对比后面值 是否大于 前面值 5 // 这里希望1 尽可能小,32 尽可能大,并接近 6 int len = nums.size(); 7 //前面小于它的最小值,没有就为极限值,保证后面没有大于它 8 vector<int> preMin(len, 1e9); 9 for(int i = 1; i < len; ++i) preMin[i] = min(preMin[i - 1], nums[i - 1]); 10 11 //后面比它小的元素中最大值 12 //倒序求,到达当前元素时后面元素已经计算结束了; //解题技巧! 13 //如果当前元素后面有大于它的元素,求当前元素 到 离它最近的大于它的元素中间 最接近它的且小于它的元素; 14 //因为如果 n + i 都不满足条件,n 元素 + (n + i) 后的元素 就更不可能满足条件 15 stack<int> s_dec; 16 for(int i = len - 1; i > 0 ; --i) { 17 int close_i_val = nums[i]; 18 while(s_dec.size() && nums[i] > s_dec.top()) { 19 close_i_val = s_dec.top(); 20 s_dec.pop(); 21 } 22 if(close_i_val < nums[i] && preMin[i] < close_i_val) return true; 23 s_dec.push(nums[i]); 24 } 25 26 return false; 27 } 28 };
18. 对于二进制中bit 位的交换操作:两边同时将无效位 通过&0 mask 掉,然后移位; 然后将两份处理过的值 | 即可;
1 class Solution { 2 public: 3 uint32_t reverseBits(uint32_t n) { 4 n = ((n & 0xFFFF0000) >> 16) | ((n & 0x0000FFFF) << 16); 5 n = ((n & 0xFF00FF00) >> 8 ) | ((n & 0x00FF00FF) << 8); 6 n = ((n & 0xF0F0F0F0) >> 4 ) | ((n & 0x0F0F0F0F) << 4); 7 n = ((n & 0xCCCCCCCC) >> 2 ) | ((n & 0x33333333) << 2); 8 n = ((n & 0xAAAAAAAA) >> 1 ) | ((n & 0x55555555) << 1); 9 10 return n; 11 } 12 };
78. 子集 : 子集枚举法:(二进制方式)
1 class Solution { 2 public: 3 void __subsets(vector<int>&nums, int idx, vector<vector<int>> &ans) { 4 int len = nums.size(); 5 if(idx == len) { 6 ans.push_back(vector<int>()); 7 return; 8 } 9 __subsets(nums, idx + 1, ans); 10 11 int cnt = ans.size(); 12 13 for(int i = 0; i < cnt; ++i) { 14 vector<int> temp(ans[i]); 15 temp.push_back(nums[idx]); 16 ans.push_back(temp); 17 } 18 return; 19 } 20 21 vector<vector<int>> subsets(vector<int>& nums) { 22 vector<vector<int>> ans; 23 //递归 24 // __subsets(nums, 0, ans); 25 26 int n = nums.size(); 27 unordered_map<int, int> mark; 28 for(int i = 0; i < n; ++i) mark[1 << i] = i; 29 for(int i = 0, I = (1 << n); i < I; ++i) { 30 vector<int> temp; 31 //直观方式 32 //for(int j = 0; j < n; ++j) if(i & (1 << j)) temp.push_back(nums[j]); 33 //树状数组类似 34 int val = i; 35 while(val) { 36 temp.push_back(nums[mark[val & (-val)]]); 37 val &= (val - 1); 38 } 39 ans.push_back(temp); 40 } 41 return ans; 42 } 43 };
19. 滚动数组;减少空间使用,常见在动态规划问题中使用
1 class Solution { 2 public: 3 int minCost(vector<vector<int>>& costs) { 4 vector<vector<int>> dp(2, costs[0]); 5 6 for(int i = 1, I = costs.size(); i < I; ++i) { 7 int cur = i % 2, pre = !cur; 8 dp[cur][0] = min(dp[pre][1], dp[pre][2]) + costs[i][0]; 9 dp[cur][1] = min(dp[pre][0], dp[pre][2]) + costs[i][1]; 10 dp[cur][2] = min(dp[pre][0], dp[pre][1]) + costs[i][2]; 11 } 12 13 int idx = (costs.size() - 1) % 2; 14 return min(dp[idx][0], min(dp[idx][1], dp[idx][2])); 15 } 16 };
20. 在二进制表示方式中,求最后一个1 的位置: x & (-x) ; 求减去最后一个1 x & (x - 1);
FenwickTree;
21. 偏移量: 当需要数组下标为 负数时候,可以通过偏移量来实现(要知道总数范围); 当然也可以用hash
494. 目标和 : 动归题
1 class Solution { 2 public: 3 int findTargetSumWays(vector<int>& nums, int target) { 4 //Rev2: 滚动数组/可达数组/偏移量 5 int sum = 0; 6 for(auto &x : nums) sum += x; 7 if(target > sum || target < -sum) return 0; 8 //偏移量,滚动数组 9 int buff[2][2 * sum + 5], *f[2] = {buff[0] + sum + 2, buff[1] + sum + 2}; 10 memset(buff, 0, sizeof(buff)); 11 f[1][0] = 1; 12 sum = 0; 13 14 for(int i = 0, I = nums.size(); i < I; ++i) { 15 int cur = i % 2, pre = !cur; 16 memset(buff[cur], 0, sizeof(buff[cur])); 17 for(int j = -sum; j <= sum; ++j) { 18 f[cur][j + nums[i]] += f[pre][j]; 19 f[cur][j - nums[i]] += f[pre][j]; 20 } 21 sum += nums[i]; 22 } 23 24 int idx = !(nums.size() % 2); 25 return f[idx][target]; 26 27 //Rev1: 滚动数组/可达数组 28 // vector<unordered_map<int,int>> dp(2); 29 // //i位置可以组成的和为nums的个数 30 // dp[0][nums[0]] += 1, dp[0][-nums[0]] += 1; 31 // for(int i = 1, I = nums.size(); i < I; ++i) { 32 // int idx = i % 2, pre = !idx; 33 // dp[idx].clear(); 34 // for(auto &x : dp[pre]) { 35 // //我到哪里去 36 // dp[idx][x.first + nums[i]] += x.second; 37 // dp[idx][x.first - nums[i]] += x.second; 38 // } 39 // } 40 // 41 // int idx = !(nums.size() % 2); 42 // return dp[idx][target]; 43 } 44 };
22. 构造法: 定义满足条件的答案的规则,然后不断实现这个规则;
1 class Solution { 2 public: 3 vector<int> grayCode(int n) { 4 if(!n) return vector<int>{0}; 5 6 int len = 1 << n; 7 n -= 1; 8 vector<int>ret(len, 0), temp = grayCode(n); 9 for(int i = 0, I = temp.size(); i < I; ++i) { 10 ret[i] = temp[i]; 11 ret[len - i - 1] = (1 << n) | temp[i]; 12 } 13 return ret; 14 } 15 }; 16 17 //another one 18 19 class Solution { 20 public: 21 //递归中要假设原来的值已经满足要求,并且后续的操作仍然要follow 这个rule 22 vector<int> grayCode(int n) { 23 vector<int> ret(1 << n, 0); 24 if(n == 0) return ret; 25 26 vector<int> ret_n_1 = grayCode(n - 1); 27 for(int i = 0, I = 1 << (n - 1); i < I; ++i) { 28 ret[i] = ret_n_1[i] << 1; 29 ret[2 * I - 1 - i] = ret[i] | 1; //这里关键是要保证交接处满足gray code 要求 30 } 31 return ret; 32 } 33 };
23. 计数数组实现下标的计算;
1 #define MAX_N 50005 2 #define lowbit(x) (x & (-x)) 3 class FenwickTree{ 4 public: 5 FenwickTree(int n = MAX_N): n(n) { 6 pc = (int *) malloc(sizeof(int) * n); 7 memset(pc, 0, sizeof(int) * n); 8 } 9 10 void update(int index, int val) { 11 while(index < n) { 12 pc[index] += val; 13 index += lowbit(index); 14 } 15 return; 16 } 17 18 int queryS(int index) { 19 int ret = 0; 20 while(index) { 21 ret += pc[index]; 22 index -= lowbit(index); 23 } 24 return ret; 25 } 26 27 private: 28 int *pc; 29 int n; 30 }; 31 class StreamRank { 32 private: 33 FenwickTree *pft; 34 35 public: 36 StreamRank() { 37 pft = new FenwickTree(); 38 } 39 40 void track(int x) { 41 pft->update(x + 1, 1); 42 return; 43 } 44 45 int getRankOfNumber(int x) { 46 return pft->queryS(x + 1); 47 } 48 }; 49 50 /** 51 * Your StreamRank object will be instantiated and called as such: 52 * StreamRank* obj = new StreamRank(); 53 * obj->track(x); 54 * int param_2 = obj->getRankOfNumber(x); 55 */
1 class Solution { 2 #define lowbit(x) (x & (-x)) 3 class FenwickTree{ 4 vector<int> c; 5 public: 6 FenwickTree(int n) : c(vector<int>(n + 1, 0)){} 7 8 void add(int index, int val) { 9 int n = c.size(); 10 while(index < n) { 11 c[index] += val; 12 index+= lowbit(index); 13 } 14 return; 15 } 16 int query(int idx) { 17 int ans = 0; 18 while(idx) { 19 ans += c[idx]; 20 idx -= lowbit(idx); 21 } 22 return ans; 23 } 24 }; 25 public: 26 vector<int> processQueries(vector<int>& queries, int m) { 27 int n = queries.size(); 28 FenwickTree tree(n + m); 29 vector<int> pos(m + 1, 0), ans(n, 0); 30 for(int j = 1; j <= m ; ++j){ 31 pos[j] = n + j; 32 tree.add(n + j, 1); 33 } 34 for(int i = 0; i < n; ++i) { 35 int index = pos[queries[i]]; 36 ans[i] = tree.query(index) - 1; 37 pos[queries[i]] = n - i; 38 tree.add(index, -1); 39 tree.add(pos[queries[i]], 1); 40 } 41 return ans; 42 } 43 }; 44 45 //original 46 class Solution { 47 public: 48 vector<int> processQueries(vector<int>& queries, int m) { 49 50 #define lowbit(x) x &(-x) 51 52 class FenwickTree{ 53 public: 54 FenwickTree(int n):size(n), c(n + 1) {} 55 void add(int ind, int val) { 56 while(ind <= size) c[ind] += val, ind += lowbit(ind); 57 return; 58 } 59 60 int query(int ind) { 61 int ans = 0; 62 while(ind) ans += c[ind], ind -= lowbit(ind); 63 return ans; 64 } 65 66 private: 67 int size; 68 vector<int> c; 69 }; 70 71 class Solution { 72 public: 73 vector<int> processQueries(vector<int>& queries, int m) { 74 vector<int> ans; 75 unordered_map<int,int> ind; 76 77 int lenq = queries.size(); 78 FenwickTree tree(m + lenq); //lenq ~ 1 每次操作的存放位置,lenq + 1~ lenq + m 存放初始值,这里都是对应的ind 位置 79 for(int i = 1; i <= m; ++i){ 80 int temp = lenq + i; 81 ind[i] = temp; //存储初始值对应的位置, 82 tree.add(temp, 1); //存储对应有值位置标为1,然后通过后面求前缀和得到对应的下标位置 83 } 84 85 for(int i = 0, I = queries.size(); i < I; ++i) { 86 int num = queries[i]; 87 ans.push_back(tree.query(ind[num]) - 1); //这里求下标,所以要把前缀和-1 88 tree.add(ind[num], -1); //原来位置把计数1 删除 89 tree.add(lenq - i, 1); //新的位置计数1 加入 90 ind[num] = lenq - i; 91 } 92 return ans; 93 //brute force 94 // unordered_map<int,int> ind(m + 1); //(num, ind) 95 // vector<int> num(m, 0), ans; 96 // 97 // for(int i = 0; i < m; ++i) { 98 // num[i] = i + 1; 99 // ind[i + 1] = i; 100 // } 101 // 102 // for(auto x : queries) { 103 // ans.push_back(ind[x]); 104 // for(int i = ind[x]; i > 0; --i) swap(num[i], num[i - 1]); 105 // for(int i = 0, I = ind[x]; i <= I; i++) ind[num[i]] = i; 106 // } 107 // 108 // return ans; 109 } 110 };