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 };
滑动窗口+二分

 76. 最小覆盖子串

 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. 数组记忆化,空间换时间;

  494. 目标和

 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 }
NIL:虚拟空节点

  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;// 实现递归过程代码;

  递归问题中,难点在于如何给予递归函数一个更好的定义,下面题目,两个递归方式实现:面试题 04.12. 求和路径 : 个人感觉第一种方案,通过两个函数的递归更加优美。

 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 };
Solution1
 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 };
solution2

 

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
stringstream

 

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 };
不同查找结果映射不同bit

 

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 掉,然后移位; 然后将两份处理过的值 | 即可;

190. 颠倒二进制位 :

 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. 滚动数组;减少空间使用,常见在动态规划问题中使用

剑指 Offer II 091. 粉刷房子

 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. 构造法: 定义满足条件的答案的规则,然后不断实现这个规则;

  89. 格雷编码

 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. 计数数组实现下标的计算;

面试题 10.10. 数字流的秩

 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  */
计数数组/fenwickTree

1409. 查询带键的排列

  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 };
计数数组记录下标

 

posted on 2022-01-02 21:35  学海一扁舟  阅读(59)  评论(0编辑  收藏  举报