算法通关之路
第2章 数学之美
1. 两数之和
-
思路+解法
-
暴力 O(n^2)
-
排序+双指针 O(nlogn)
class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { int size = nums.size(); vector<vector<int>> vec(size, vector<int>(2)); for(int i = 0; i < size; i++){ vec[i][0] = nums[i]; vec[i][1] = i; } sort(vec.begin(), vec.end()); int l = 0, r = size - 1; while(l < r){ if(vec[l][0] + vec[r][0] == target){ return vector<int>{vec[l][1], vec[r][1]}; } else if(vec[l][0] + vec[r][0] > target){ r--; } else if(vec[l][0] + vec[r][0] < target){ l++; } } return vector<int>{}; } };
-
哈希表 O(n)
class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { unordered_map<int, int> m; vector<int> ans; for(int i = 0; i < nums.size(); i++){ if(m.find(target - nums[i]) != m.end()){ return vector<int>{m[target - nums[i]], i}; } m[nums[i]] = i; } return ans; } };
-
-
一点收获
- find()找的是键,不是值
15. 三数之和
-
思路+解法
-
排序+双指针 O(n^2)
class Solution { public: vector<vector<int>> threeSum(vector<int>& nums) { int size = nums.size(); sort(nums.begin(), nums.end()); vector<vector<int>> ans; for(int i = 0; i < size; i++){ //第一个元素大于0的剪枝很巧妙 if(nums[i] > 0) return ans; //去重 if(i > 0 && nums[i] == nums[i-1]) continue; int j = i + 1, k = size - 1; while(j < k){ if(nums[j] + nums[k] == -nums[i]){ ans.push_back({nums[i], nums[j], nums[k]}); j++; k--; //去重 while(j < k && nums[j] == nums[j - 1]) j++; while(j < k && nums[k] == nums[k + 1]) k--; } else if(nums[j] + nums[k] > -nums[i]){ k--; } else if(nums[j] + nums[k] < -nums[i]){ j++; } } } return ans; } };
-
18. 四数之和
-
思路+解法
-
排序+双指针 O(n^3)
class Solution { public: vector<vector<int>> fourSum(vector<int>& nums, int target) { sort(nums.begin(), nums.end()); vector<vector<int>> ans; int size = nums.size(); for(int i = 0; i < size; i++){ if(i > 0 && nums[i] == nums[i-1]) continue; for(int j = i + 1; j < size; j++){ if(j > i + 1 && nums[j] == nums[j-1]) continue; int l = j + 1, r = size - 1; while(l < r){ //合超int范围 if((long long)nums[i] + nums[j] + nums[l] + nums[r] == target){ ans.push_back({nums[i], nums[j], nums[l], nums[r]}); l++; r--; while(l < r && nums[l] == nums[l - 1]) l++; while(l < r && nums[r] == nums[r + 1]) r--; } else if((long long)nums[i] + nums[j] + nums[l] + nums[r] > target){ r--; } else if((long long)nums[i] + nums[j] + nums[l] + nums[r] < target){ l++; } } } } return ans; } };
-
一点剪枝优化(140ms->8ms)
class Solution { public: vector<vector<int>> fourSum(vector<int>& nums, int target) { sort(nums.begin(), nums.end()); vector<vector<int>> ans; int size = nums.size(); for(int i = 0; i < size - 3; i++){ //剪枝 if ((long long)nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) break; if ((long long)nums[i] + nums[size - 3] + nums[size - 2] + nums[size - 1] < target) continue; //去重 if(i > 0 && nums[i] == nums[i-1]) continue; for(int j = i + 1; j < size - 2; j++){ //剪枝 if ((long long)nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target) break; if ((long long)nums[i] + nums[j] + nums[size - 2] + nums[size - 1] < target) continue; //去重 if(j > i + 1 && nums[j] == nums[j-1]) continue; int l = j + 1, r = size - 1; while(l < r){ //合超int范围 if((long long)nums[i] + nums[j] + nums[l] + nums[r] == target){ ans.push_back({nums[i], nums[j], nums[l], nums[r]}); l++; r--; while(l < r && nums[l] == nums[l - 1]) l++; while(l < r && nums[r] == nums[r + 1]) r--; } else if((long long)nums[i] + nums[j] + nums[l] + nums[r] > target){ r--; } else if((long long)nums[i] + nums[j] + nums[l] + nums[r] < target){ l++; } } } } return ans; } };
-
454. 四数相加 II
-
思路+解法
-
哈希表 O(n^2)
class Solution { public: int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) { int ans = 0; unordered_map<int, int> m; for(auto x : nums1){ for(auto y : nums2){ m[x+y]++; } } for(auto x : nums3){ for(auto y : nums4){ if(m.find(-(x + y)) != m.end()) ans += m[-(x + y)]; } } return ans; } };
-
16. 最接近的三数之和
-
思路+解法
-
排序+双指针 O(n^2)
class Solution { public: int threeSumClosest(vector<int>& nums, int target) { sort(nums.begin(), nums.end()); int size = nums.size(); int ans = 100000; for(int i = 0; i < size; i++){ //剪枝 if(i > 0 && nums[i] == nums[i - 1]) continue; int l = i + 1, r = size - 1; while(l < r){ if(nums[i] + nums[l] + nums[r] == target){ return target; } else{ //计算当前值是否靠近 if(abs(nums[i] + nums[l] + nums[r] - target) < abs(ans - target)){ ans = nums[i] + nums[l] + nums[r]; } //双指针移动 if(nums[i] + nums[l] + nums[r] > target) { r--; //剪枝 while(l < r && nums[r] == nums[r + 1]) r--; } else if(nums[i] + nums[l] + nums[r] < target){ l++; //剪枝 while(l < r && nums[l] == nums[l-1]) l++; } } } } return ans; } };
-
-
一点收获:
- 和三数之和稍有不同,计算最小差值是在不等于target时计算,而不是括号外,不然会导致双指针移动不明
53. 最大子序和(*分治法)
-
思路+解法
-
双指针滑动窗口 O(n)
class Solution { public: int maxSubArray(vector<int>& nums) { int ans = nums[0]; int l = 0, r = 1; int now = nums[0]; while(r < nums.size()){ now += nums[r]; while(l < r && now < nums[r]){ now -= nums[l]; l++; } r++; ans = max(now, ans); } return ans; } };
-
动态规划 O(n)
class Solution { public: int maxSubArray(vector<int>& nums) { int ans = nums[0]; vector<int> dp(nums.size(), 0); dp[0] = nums[0]; for(int i = 1; i < nums.size(); i++){ dp[i] = max(nums[i], dp[i - 1] + nums[i]); ans = max(dp[i], ans); } return ans; } };
-
分治法 O(nlogn)
-
前缀和 O(n)
class Solution { public: int maxSubArray(vector<int>& nums) { int ans = nums[0]; int minPrefix = 0; int sum = 0; for(int i = 0; i < nums.size(); i++){ sum += nums[i]; ans = max(ans, sum - minPrefix); minPrefix = min(minPrefix, sum); } return ans; } };
-
-
一点收获
- 一题多解,多思考拓展思维
179. 最大数
-
思路+解法
-
排序 O*(*nlognlogm)
class Solution { public: static bool cmp(string a, string b){ return a + b > b + a; } string largestNumber(vector<int>& nums) { vector<string> strs; for(int i = 0; i < nums.size(); i++){ strs.push_back(to_string(nums[i])); } sort(strs.begin(), strs.end(), cmp); string ans = ""; for(string s : strs){ ans += s; } if(ans[0] == '0') return "0"; return ans; } };
-
-
一点收获
- 前导0
166. 分数到小数
-
思路+解法:
-
比较余数 O(l) l是答案的长度
class Solution { public: string fractionToDecimal(int numerator, int denominator) { string ans =""; if((long long)numerator * denominator < 0) ans += '-'; long long a = abs(numerator); long long b = abs(denominator); ans += to_string(a / b); a = a % b; if(a != 0) ans += "."; unordered_map<long long, int> m; int loc = ans.size(); //小数的第几位 m[a] = loc++; while(a > 0){ a *= 10; //得到的数 long long x = a / b; ans += to_string(x); //这一步的余数 a -= x * b; if(m.find(a) != m.end()){ ans.insert(ans.begin() + m[a], '('); ans += ')'; return ans; } m[a] = loc++; } return ans; } };
-
-
一点收获
- 负数转绝对值越界问题
368. 最大整除子集
-
思路+解法
-
动态规划 O(n^2)
class Solution { public: vector<int> largestDivisibleSubset(vector<int>& nums) { sort(nums.begin(), nums.end()); int size = nums.size(); vector<int> ans; vector<int> dp(size, 1); //当前位置的前一个元素 vector<int> pre(size, -1); //整除子集的最大长度 int maxlen = 1; int maxloc = 0; for(int i = 0; i < nums.size(); i++){ for(int j = 0; j < i; j++){ if(nums[i] % nums[j] == 0){ if(dp[j] + 1 > dp[i]){ dp[i] = dp[j] + 1; pre[i] = j; } } } if(dp[i] > maxlen){ maxlen = dp[i]; maxloc = i; } } ans.push_back(nums[maxloc]); int loc = pre[maxloc]; //回溯找答案 while(loc != -1){ ans.push_back(nums[loc]); loc = pre[loc]; } return ans; } };
-
1175. 质数排列
-
思路+解法
-
埃氏筛法 O(nloglogn)
class Solution { private: vector<int> is_prime = vector<int>(101, true); public: int sieve(int n){ int p = 0; is_prime[0] = is_prime[1] = false; for(int i = 2; i <= n; i++){ if(is_prime[i]){ p++; for(int j = i * 2; j <= n; j += i){ is_prime[j] = false; } } } return p; } int numPrimeArrangements(int n) { int k1 = sieve(n); int k2 = n - k1; long ans = 1; for(int i = 2; i <= k1; i++){ ans = (ans * i) % 1000000007; } for(int i = 2; i <= k2; i++){ ans = (ans * i) % 1000000007; } return ans; } };
-
第3章 回文的艺术
680. 验证回文字符串 Ⅱ
-
思路+解法:
-
双指针 O(n)
class Solution { public: bool isPalindrome(string & s, int l, int r){ while(l < r){ if(s[l] != s[r]) return false; l++ ,r--; }; return true; } bool validPalindrome(string s) { int size = s.size(); int l = 0, r = size - 1; int cnt = 0; while(l < r){ if(s[l] != s[r]){ return isPalindrome(s, l + 1, r) || isPalindrome(s, l, r - 1); } l++ ,r--; } return true; } };
-
-
一点收获:
- 认真思考时间复杂度
234. 回文链表(*)
-
思路+解法:
-
快慢指针翻转后半部分后比较 O(n)
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ class Solution { public: bool isPalindrome(ListNode* head) { if(head->next == nullptr) return true; //快慢指针找到前半部分的结尾 ListNode* slow = head; ListNode* fast = head; while(fast->next != nullptr && fast->next->next != nullptr){ fast = fast->next->next; slow = slow->next; } //翻转后半部分 ListNode* pre = slow->next; ListNode* cur = slow->next->next; pre->next = nullptr; //! while(cur != nullptr){ ListNode* temp = cur->next; cur->next = pre; pre = cur; cur = temp; } //回文比较 ListNode* firstHalfHead = head; ListNode* secondHalfHead = pre; while(secondHalfHead != nullptr){ if(firstHalfHead->val != secondHalfHead->val) return false; firstHalfHead = firstHalfHead->next; secondHalfHead = secondHalfHead->next; } return true; } };
-
翻转前半部分* O(n)
-
-
一点收获:
- 链表操作
9. 回文数
-
思路+解法:
-
只翻转一半数字 O(logn)
class Solution { public: bool isPalindrome(int x) { if(x < 0) return false; if(x == 0) return true; if(x % 10 == 0) return false; int rev_num = 0; while(x > rev_num){ rev_num = rev_num * 10 + x % 10; x /= 10; } return x == rev_num || x == rev_num / 10; } };
-
5. 最长回文子串(*)
-
思路+解法
-
动态规划 O(n^2)
class Solution { public: string longestPalindrome(string s) { int n = s.size(); //dp数组含义:从i到j的字符串是否为回文子串 vector<vector<bool>> dp(n, vector<bool>(n, false)); for(int i = 0; i < n; i++){ dp[i][i] = 0; } int max_len = 1; int begin = 0; //画表看遍历顺序 for(int i = n - 2; i >= 0; i--){ for(int j = i + 1; j < n; j++){ if(s[i] == s[j]){ if(j - i <= 2){ dp[i][j] = true; } else{ if(dp[i + 1][j - 1]) dp[i][j] = true; } } if(dp[i][j]){ if(j - i + 1 > max_len){ max_len = j - i + 1; begin = i; } } } } return s.substr(begin, max_len); } };
-
中心扩展 O(n^2)
class Solution { public: int n = 0; int extend(string& s, int left, int right){ while(right < n && left >= 0 && s[left] == s[right]){ left--; right++; } return right - left - 1; } string longestPalindrome(string s) { n = s.size(); int max_len = 1; int start = 0; for(int i = 0; i < n; i++){ int len1 ; len1 = extend(s, i, i); int len2; len2 = extend(s, i, i + 1); int len = max(len1, len2); if(len > max_len){ max_len = len; start = i - (len - 1) / 2; } } return s.substr(start, max_len); } };
-
马拉车
-
516. 最长回文子序列
-
思路+解法
-
动态规划 O(n^2)
class Solution { public: int longestPalindromeSubseq(string s) { int n = s.size(); int ans = 1; vector<vector<int>> dp(n, vector<int>(n, 0)); for(int i = 0; i < n; i++) dp[i][i] = 1; for(int i = n - 2; i >= 0; i--){ for(int j = i + 1; j < n; j++){ if(s[i] == s[j]){ dp[i][j] = dp[i + 1][j - 1] + 2; } else{ dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]); } } } return dp[0][n - 1]; } };
-
906. 超级回文数
-
思路+解法
-
构造回文数 O(W1/4∗logW)
class Solution { public: bool isPalindrome(long long x){ if(x % 10 == 0) return false; long long rev_num = 0; while(x > rev_num){ rev_num = rev_num * 10 + x % 10; x /= 10; } return x == rev_num || x == rev_num / 10; } int superpalindromesInRange(string left, string right) { int ans = 0; long long l = stol(left); long long r = stol(right); //ABBA for(int i = 1; i < 100000; i++){ string s1 = to_string(i); string s2 = s1; reverse(s2.begin(), s2.end()); string s = s1 + s2; long m = stol(s); m *= m; if(m > r) break; if(m >= l && isPalindrome(m)){ ans++; } } //ABA for(int i = 1; i < 100000; i++){ string s1 = to_string(i); string s2 = s1; reverse(s2.begin(), s2.end()); string s = s1.append(s2.begin() + 1, s2.end()); long m = stol(s); m *= m; if(m > r) break; if(m >= l && isPalindrome(m)){ ans++; } } return ans; } };
-
第5章 深度优先遍历和广度优先遍历
112. 路径总和
-
思路+解法:
-
DFS递归 O(n)
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */ class Solution { public: bool dfs(TreeNode* node, int sum, int tar){ if(!node) return false; sum += node->val; if(!node->left && !node->right){ if(sum == tar) return true; else return false; } return dfs(node->left, sum, tar) || dfs(node->right, sum, tar); } bool hasPathSum(TreeNode* root, int targetSum) { return dfs(root, 0, targetSum); } };
-
113. 路径总和 II
-
思路+解法:
-
DFS O(n)
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */ class Solution { public: int target; vector<vector<int>> ans; vector<int> path; void dfs(TreeNode* node, int sum){ if(!node->left && !node->right && sum == target){ ans.push_back(path); return; } if(node->left){ path.push_back(node->left->val); dfs(node->left, sum + node->left->val); path.pop_back(); } if(node->right){ path.push_back(node->right->val); dfs(node->right, sum + node->right->val); path.pop_back(); } } vector<vector<int>> pathSum(TreeNode* root, int targetSum) { if(!root) return ans; target = targetSum; ans.clear(); path.clear(); path.push_back(root->val); dfs(root, root->val); return ans; } };
-
124. 二叉树中的最大路径和
-
思路+解法:
DFS后序遍历 O(n)
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */ class Solution { public: int ans = INT_MIN; //后序遍历 //每个节点拥有自己的最大值,可以同时拥有左右节点 //但是给父节点返回的只能为一条路径 int dfs(TreeNode* node){ if(!node) return 0; int left_sum = dfs(node->left); int right_sum = dfs(node->right); int node_max = node->val; node_max += left_sum > 0? left_sum : 0; node_max += right_sum > 0? right_sum : 0; ans = max(node_max, ans); return max(node->val, max(node->val + left_sum, node->val + right_sum)); } int maxPathSum(TreeNode* root) { dfs(root); return ans; } };
岛屿问题
第6章 二分法
704. 二分查找
-
思路+解法:
-
二分模板 O(logn)
class Solution { public: int search(vector<int>& nums, int target) { int l = 0, r = nums.size() - 1; while(l <= r){ int mid = (r - l) / 2 + l; if(nums[mid] == target) return mid; else if(nums[mid] > target){ r = mid - 1; }else if(nums[mid] < target){ l = mid + 1; } } return -1; } };
-
153. 寻找旋转排序数组中的最小值
-
解法+思路:
-
二分查找 O(logn)
class Solution { public: int findMin(vector<int>& nums) { int l = 0, r = nums.size() - 1; while(l <= r){ int mid = (r - l) / 2 + l; if((mid == 0 || nums[mid] < nums[mid - 1]) &&(mid == nums.size() - 1 || nums[mid] < nums[mid + 1])) return nums[mid]; else if(nums[mid] > nums[r]){ l = mid + 1; } else { r = mid - 1; } } return -1; } };
-
875. 爱吃香蕉的珂珂
思路+解法:
-
二分答案 O(logn)
class Solution { public: int minEatingSpeed(vector<int>& piles, int h) { int l = 1, r = pow(10 ,9); int ans = 1; while(l <= r){ int mid = (r - l)/2 + l; int sum = 0; for(int i = 0; i < piles.size(); i++){ sum += piles[i] / mid; if(piles[i] % mid != 0) sum++; } if(sum > h){ l = mid + 1; } else if(sum <= h){ ans = mid; r = mid - 1; } } return ans; } };
69. Sqrt(x)
-
思路+解法:
-
二分答案 O(logn)
class Solution { public: int mySqrt(int x) { int l = 0, r = x; int ans = 0; while(l <= r){ int mid = (r - l) / 2 + l; if((long long)mid * mid <= x){ l = mid + 1; ans = mid; } else{ r = mid - 1; } } return ans; } };
-
162. 寻找峰值
-
思路+解法:
-
二分查找 O(logn)
class Solution { public: int findPeakElement(vector<int>& nums) { int l = 0, r = nums.size() - 1; while(l <= r){ int mid = (r - l) / 2 + l; if((mid == 0 || nums[mid] > nums[mid - 1]) &&(mid == nums.size() - 1 || nums[mid] > nums[mid + 1])) return mid; if(mid == 0 || nums[mid] > nums[mid - 1] ){ l = mid + 1; } else if(nums[mid] < nums[mid - 1]){ r = mid - 1; } } return -1; } };
-
410. 分割数组的最大值
-
思路+解法
-
二分答案 O(nlog(sum - max_num))
class Solution { public: bool cal(vector<int>& nums, int max_sum, int m){ int _m = 0; int temp_sum = 0; for(int x : nums){ if(temp_sum + x > max_sum){ _m++; temp_sum = x; } else temp_sum += x; } if(temp_sum > 0) _m++; return _m <= m; } int splitArray(vector<int>& nums, int m) { int sum = 0; int max_num = INT_MIN; for(int x : nums) { sum += x; max_num = max(max_num, x); } //二分区间为最大的元素到所有元素的和 int l = max_num, r = sum; int ans = 0; while(l <= r){ int mid = (r - l) / 2 + l; if(cal(nums, mid, m)){ ans = mid; r = mid - 1; } else{ l = mid + 1; } } return ans; } };
-
第7章 位运算
191. 位1的个数
-
思路+解法:
-
O()
-
371. 两整数之和
-
思路+解法:
-
O()
-
397. 整数替换
-
思路+解法:
-
O()
-
136. 只出现一次的数字
-
思路+解法:
-
异或 O(n)
class Solution { public: int singleNumber(vector<int>& nums) { int ans = 0; for(int& x : nums) ans ^= x; return ans; } };
-
第8章 设计
155. 最小栈
-
思路+解法:
-
辅助栈 O(1) O(n)
class MinStack { public: stack<int> s; stack<int> assist_s; MinStack() { } void push(int val) { s.push(val); if(assist_s.empty() || assist_s.top() > val) assist_s.push(val); else if(assist_s.top() <= val) assist_s.push(assist_s.top()); } void pop() { s.pop(); assist_s.pop(); } int top() { return s.top(); } int getMin() { return assist_s.top(); } }; /** * Your MinStack object will be instantiated and called as such: * MinStack* obj = new MinStack(); * obj->push(val); * obj->pop(); * int param_3 = obj->top(); * int param_4 = obj->getMin(); */
-
数组中存的是差值 O(1)空间复杂度
class MinStack { public: stack<long long> s; long long min_value; MinStack() { } void push(int val) { if(s.empty()){ min_value = val; s.push(0); }else{ s.push(val - min_value); if(val - min_value < 0){ min_value = val; } } } void pop() { if(s.top() > 0){ s.pop(); }else{ min_value = min_value - s.top(); s.pop(); } } int top() { if(s.top() > 0){ return min_value + s.top(); }else{ return min_value; } } int getMin() { return min_value; } }; /** * Your MinStack object will be instantiated and called as such: * MinStack* obj = new MinStack(); * obj->push(val); * obj->pop(); * int param_3 = obj->top(); * int param_4 = obj->getMin(); */
-
208. 实现 Trie (前缀树)
-
思路+解法:
-
Trie树
const int N = 1e5+50; bool isEnd[N]; int son[N][26]; int idx = 0; class Trie { public: /** Initialize your data structure here. */ Trie() { memset(isEnd,false, sizeof(isEnd)); memset(son, 0, sizeof(son)); } /** Inserts a word into the trie. */ void insert(string word) { int p = 0; for(auto x: word){ int u = x - 'a'; if(!son[p][u]){ son[p][u] = ++idx; //默认节点是0,所以前置++ } p = son[p][u]; //移动到下一个节点编号 } isEnd[p] = true; //标记结尾 } /** Returns if the word is in the trie. */ bool search(string word) { int p = 0; for(auto x: word){ int u = x - 'a'; if(!son[p][u]){ return false; } p = son[p][u]; } return isEnd[p]; } /** Returns if there is any word in the trie that starts with the given prefix. */ bool startsWith(string prefix) { int p = 0; for(auto x : prefix){ int u = x - 'a'; if(!son[p][u]){ return false; } p = son[p][u]; } return true; } }; /** * Your Trie object will be instantiated and called as such: * Trie* obj = new Trie(); * obj->insert(word); * bool param_2 = obj->search(word); * bool param_3 = obj->startsWith(prefix); */
-
-
亿点收获:
- 非静态私有数据成员不能初始化
146. LRU 缓存机制
-
思路+解法:
-
哈希表 + 链表:O(1)
struct Node{ int key, val; Node* pre; Node* next; Node(int k, int v): key(k), val(v), pre(nullptr), next(nullptr) {} Node(): key(0), val(0), pre(nullptr), next(nullptr) {} }; class LRUCache { private: unordered_map<int ,Node*> m; int size, capacity; Node* drummyHead; Node* drummyTail; public: LRUCache(int capacity) { size = 0; this -> capacity = capacity; drummyHead = new Node(); drummyTail = new Node(); drummyHead->pre = nullptr; drummyHead->next = drummyTail; drummyTail->pre = drummyHead; drummyTail->next = nullptr; } int get(int key) { //cout << "get:"<< key << endl; if(m.find(key) != m.end()){ Node* node = m[key]; move_to_head(node); return node->val; } return -1; } void put(int key, int value) { //cout << "put:"<< key; if(m.find(key) != m.end()){ Node* node = m[key]; move_to_head(node); node->val = value; } else{ Node* node = new Node(key, value); m[key] = node; if(size == capacity){ delete_tail(); add_to_head(node); }else{ add_to_head(node); size++; } } /*for(Node* b = drummyHead->next; b != drummyTail; b = b->next) cout << b->val << " "; cout << m.size(); cout << endl;*/ } void move_to_head(Node* node){ Node* prenode = node->pre; Node* nextnode = node->next; prenode->next = nextnode; nextnode->pre = prenode; Node* headnextnode = drummyHead->next; drummyHead->next = node; node->pre = drummyHead; node->next = headnextnode; headnextnode->pre = node; } void add_to_head(Node* node){ Node* headnextnode = drummyHead->next; drummyHead->next = node; node->pre = drummyHead; node->next = headnextnode; headnextnode->pre = node; } void delete_tail(){ Node* tailpreprenode = drummyTail->pre->pre; Node* tailprenode = drummyTail->pre; tailpreprenode->next = drummyTail; drummyTail->pre = tailpreprenode; m.erase(tailprenode->key); delete tailprenode; } }; /** * Your LRUCache object will be instantiated and called as such: * LRUCache* obj = new LRUCache(capacity); * int param_1 = obj->get(key); * obj->put(key,value); */
-
460. LFU 缓存
- 思路+解法:
- https://leetcode-cn.com/problems/lfu-cache/solution/ha-xi-biao-shuang-xiang-lian-biao-c-by-l-e5e0/
1206. 设计跳表
第13章 股票问题
121. 买卖股票的最佳时机
-
思路+解法:
-
贪心 O(n)
class Solution { public: int maxProfit(vector<int>& prices) { int buy = prices[0]; int ans = 0; int n = prices.size(); for(int i = 1; i < n; ++i){ if(prices[i] > buy) ans = max(ans, prices[i] - buy); buy = min(buy, prices[i]); } return ans; } };
-
动态规划 O(n)
class Solution { public: int maxProfit(vector<int>& prices) { int n = prices.size(); //持有股票、不持有股票时手中的现金量 vector<vector<int>> dp(n, vector<int>(2)); dp[0][0] = -prices[0]; dp[0][1] = 0; for(int i = 1; i < n; i++){ dp[i][0] = max(dp[i - 1][0], -prices[i]); dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]); } return dp[n - 1][1]; } };
-
122. 买卖股票的最佳时机 II
-
思路+解法:
-
动态规划 O(n)
class Solution { public: int maxProfit(vector<int>& prices) { int n = prices.size(); vector<vector<int>> dp(n, vector(2, 0)); dp[0][0] = -prices[0]; dp[0][1] = 0; for(int i = 1; i < n; i++){ dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]); dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]); } return dp[n - 1][1]; } };
-
贪心 O(n)
-
123. 买卖股票的最佳时机 III
-
思路+解法:
-
动态规划 O(n)
class Solution { public: int maxProfit(vector<int>& prices) { int n = prices.size(); vector<vector<int>> dp(n, vector<int>(5, 0)); dp[0][0] = 0; dp[0][1] = -prices[0]; dp[0][2] = 0; dp[0][3] = -prices[0]; dp[0][4] = 0; for(int i = 1; i < n; i++){ dp[i][0] = dp[i - 1][0]; dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]); dp[i][2] = max(dp[i - 1][2], dp[i - 1][1] + prices[i]); dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i]); dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]); } return dp[n - 1][4]; } };
-
188. 买卖股票的最佳时机 IV
-
思路+解法:
-
动态规划 O(n * k)
class Solution { public: int maxProfit(int k, vector<int>& prices) { if (prices.size() == 0) return 0; int n = prices.size(); vector<vector<int>> dp(n, vector<int>(2 * k + 1, 0)); dp[0][0] = 0; for(int i = 1; i < 2 * k + 1; i += 2){ dp[0][i] = -prices[0]; dp[0][i + 1] = 0; } for(int i = 1; i < n; i++){ dp[i][0] = dp[i - 1][0]; for(int j = 1; j < 2 * k + 1; j += 2){ dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1] - prices[i]); dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] + prices[i]); } } return dp[n - 1][2 * k]; } };
-
309. 最佳买卖股票时机含冷冻期
714. 买卖股票的最佳时机含手续费
-
思路+解法:
-
动态规划 O(n)
class Solution { public: int maxProfit(vector<int>& prices, int fee) { int n = prices.size(); vector<vector<int>> dp(n, vector<int>(2, 0)); dp[0][0] = -prices[0] - fee; dp[0][1] = 0; for(int i = 1; i < n; i++){ dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i] - fee); dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i]); } return dp[n - 1][1]; } };
-