├── .gitignore ├── ML ├── 机器学习.md ├── 正则化.md └── 网络模型.md ├── MyCoder ├── ACM │ ├── 2024 │ │ ├── 2024秋招.md │ │ ├── DJ-1.cpp │ │ ├── JD-1.cpp │ │ ├── MT-01.cpp │ │ ├── MT-02.cpp │ │ └── ZY-1.cpp │ ├── KamaCoder │ │ ├── 29.cpp │ │ └── 网址.md │ └── acm.cpp ├── LeetCode │ ├── longest-substring-without-repeating-characters.cpp │ └── median-of-two-sorted-arrays.cpp ├── MyCode.md └── Zuo │ ├── 10 │ ├── 10_01.cpp │ └── 10_05.cpp │ ├── 13 │ ├── 13_01.cpp │ └── 13_02.cpp │ ├── 16 │ └── 16_05.cpp │ ├── 17 │ ├── 17_02.cpp │ ├── 17_04.cpp │ └── 17_05.cpp │ ├── 18 │ └── 18_02.cpp │ ├── 19 │ ├── 19_02.cpp │ └── 19_04.cpp │ ├── 22 │ ├── 22_02.cpp │ └── 22_03.cpp │ ├── 23 │ ├── 23_04.cpp │ ├── 23_05.cpp │ └── majority-element-ii.cpp │ ├── 24 │ ├── 24_05.cpp │ └── 24_06.cpp │ ├── 26 │ └── 20_01.cpp │ ├── 27 │ └── 27_02.cpp │ ├── 29 │ ├── 29_02.cpp │ ├── 29_06.cpp │ └── 29_07.cpp │ ├── 01 │ ├── 01_01.cpp │ ├── 01_04.cpp │ └── 01_07.cpp │ ├── 02 │ ├── 02_04.cpp │ └── 02_05.cpp │ ├── 03 │ ├── 03_01.cpp │ ├── 03_04.cpp │ └── 03_05.cpp │ ├── 04 │ ├── 04-07.cpp │ ├── 04_02.cpp │ └── 04_05.cpp │ ├── 05 │ ├── 05_02.cpp │ └── 05_03.cpp │ ├── 08 │ ├── 08_01.cpp │ └── 08_02.cpp │ ├── 09 │ ├── 09_02.cpp │ ├── 09_03.cpp │ └── 09_05.cpp │ └── 题目编号.md ├── PCL └── 滤波采样算法.md ├── SLAM ├── Eigen │ └── Test-Matrix.cpp ├── SLAM.md ├── optimize │ ├── Gauss-Newton.cpp │ └── L-M.cpp ├── 参考.md └── 空间表述.md ├── cpp ├── C++11.md ├── code │ ├── test_callback.cpp │ ├── test_class.cpp │ ├── test_const.cpp │ ├── test_smart_ptr.cpp │ ├── test_static.cpp │ ├── test_stl.cpp │ ├── test_thread.cpp │ └── test_type.cpp ├── 一些函数.md ├── 关键字.md ├── 内存管理.md ├── 函数.md ├── 函数指针和回调函数.md ├── 多线程.md ├── 宏.md ├── 数据类型.md ├── 模板STL.md └── 面向对象.md ├── readme.md ├── test ├── test.cpp ├── 数学概念 ├── 优化.md ├── 各种距离.md ├── 概率.md ├── 流形.md ├── 滤波.md ├── 矩阵.md └── 群代数.md ├── 算法学习 ├── 1. Algorithm │ ├── help │ │ ├── help.md │ │ └── 前缀和数组 │ │ │ ├── PositivesEqualsNegtivesLongestSubarray.cpp │ │ │ ├── find-the-longest-substring-containing-vowels-in-even-counts.cpp │ │ │ ├── longest-well-performing-interval.cpp │ │ │ ├── make-sum-divisible-by-p.cpp │ │ │ ├── range-sum-query-immutable.cpp │ │ │ └── subarray-sum-equals-k.cpp │ ├── test00.cpp │ ├── 二分算法 │ │ ├── RobotPassThroughBuilding.cpp │ │ ├── find-k-th-smallest-pair-distance.cpp │ │ ├── koko-eating-bananas.cpp │ │ ├── maximum-running-time-of-n-computers.cpp │ │ ├── split-array-largest-sum.cpp │ │ └── 二分.md │ ├── 位运算 │ │ ├── test.cpp │ │ └── 按位操作.md │ ├── 坑.md │ ├── 排序算法 │ │ ├── sort.cpp │ │ └── 排序算法.md │ ├── 数据结构.md │ ├── 算法.md │ ├── 运算操作.md │ └── 递归算法 │ │ ├── hanota-lcci.cpp │ │ ├── n-queens-ii.cpp │ │ ├── n-queens.cpp │ │ └── 递归算法.md ├── 10. GreedyAlgorithm │ ├── assign-cookies.cpp │ ├── best-time-to-buy-and-sell-stock-ii.cpp │ ├── binary-tree-cameras.cpp │ ├── candy.cpp │ ├── gas-station.cpp │ ├── jump-game-ii.cpp │ ├── jump-game.cpp │ ├── lemonade-change.cpp │ ├── maximize-sum-of-array-after-k-negations.cpp │ ├── maximum-subarray.cpp │ ├── merge-intervals.cpp │ ├── minimum-number-of-arrows-to-burst-balloons.cpp │ ├── monotone-increasing-digits.cpp │ ├── non-overlapping-intervals.cpp │ ├── partition-labels.cpp │ ├── queue-reconstruction-by-height.cpp │ ├── wiggle-subsequence.cpp │ └── 贪心算法.md ├── 11.DynamicProgramming │ ├── 01背包问题 │ │ ├── last-stone-weight-ii.cpp │ │ ├── partition-equal-subset-sum.cpp │ │ └── target-sum.cpp │ ├── Subsequence │ │ ├── delete-operation-for-two-strings.cpp │ │ ├── distinct-subsequences.cpp │ │ ├── edit-distance.cpp │ │ ├── is-subsequence.cpp │ │ ├── longest-common-subsequence.cpp │ │ ├── longest-continuous-increasing-subsequence.cpp │ │ ├── longest-increasing-subsequence.cpp │ │ ├── longest-palindromic-subsequence.cpp │ │ ├── maximum-length-of-repeated-subarray.cpp │ │ ├── maximum-subarray.cpp │ │ ├── palindromic-substrings.cpp │ │ └── uncrossed-lines.cpp │ ├── climbing-stairs.cpp │ ├── fibonacci-number.cpp │ ├── integer-break.cpp │ ├── min-cost-climbing-stairs.cpp │ ├── ones-and-zeroes.cpp │ ├── stickers-to-spell-word.cpp │ ├── unique-binary-search-trees.cpp │ ├── unique-paths-ii.cpp │ ├── unique-paths.cpp │ ├── 动态规划.md │ ├── 完全背包问题 │ │ ├── coin-change-ii.cpp │ │ ├── coin-change.cpp │ │ ├── combination-sum-iv.cpp │ │ ├── perfect-squares.cpp │ │ └── word-break.cpp │ ├── 打家劫舍问题 │ │ ├── house-robber-ii.cpp │ │ ├── house-robber-iii.cpp │ │ └── house-robber.cpp │ └── 购买股票问题 │ │ ├── best-time-to-buy-and-sell-stock-ii.cpp │ │ ├── best-time-to-buy-and-sell-stock-iii.cpp │ │ ├── best-time-to-buy-and-sell-stock-iv.cpp │ │ ├── best-time-to-buy-and-sell-stock-with-cooldown.cpp │ │ ├── best-time-to-buy-and-sell-stock-with-transaction-fee.cpp │ │ └── best-time-to-buy-and-sell-stock.cpp ├── 12.GraphAlgorithm │ ├── Dijkstra │ │ ├── bfs.cpp │ │ └── dijkstra.cpp │ ├── UnionSet │ │ ├── MergeUser.cpp │ │ ├── MyUnionSet.cpp │ │ └── 并查集.md │ ├── bfs │ │ ├── keys-and-rooms.cpp │ │ ├── number-of-islands.cpp │ │ └── surrounded-regions.cpp │ ├── dfs │ │ └── number-of-islands.cpp │ └── 图算法.md ├── 2. Array │ ├── binary-search.cpp │ ├── minimum-size-subarray-sum.cpp │ ├── remove-duplicates-from-sorted-array.cpp │ ├── remove-element.cpp │ ├── spiral-matrix-ii.cpp │ ├── squares-of-a-sorted-array.cpp │ └── 数组.md ├── 3. NodeList │ ├── design-linked-list.cpp │ ├── 删除元素 │ │ ├── remove-linked-list-elements.cpp │ │ └── remove-nth-node-from-end-of-list.cpp │ ├── 复制链表 │ │ └── copy-list-with-random-pointer.cpp │ ├── 相交、环 │ │ ├── intersection-of-two-linked-lists-lcci.cpp │ │ ├── linked-list-cycle-ii.cpp │ │ └── linked-list-cycle.cpp │ ├── 翻转链表 │ │ ├── reverse-linked-list.cpp │ │ ├── reverse-nodes-in-k-group │ │ ├── reverse-nodes-in-k-group.cpp │ │ └── swap-nodes-in-pairs.cpp │ ├── 链表.md │ ├── 链表合并 │ │ └── merge-two-sorted-lists.cpp │ └── 链表求和 │ │ └── sum-lists-lcci.cpp ├── 4.HashMap │ ├── 3sum.cpp │ ├── 4sum-ii.cpp │ ├── 4sum.cpp │ ├── happy-number.cpp │ ├── intersection-of-two-arrays.cpp │ ├── ransom-note.cpp │ ├── two-sum.cpp │ ├── valid-anagram.cpp │ └── 哈希表.md ├── 5.String │ ├── find-the-index-of-the-first-occurrence-in-a-string.cpp │ ├── repeated-substring-pattern.cpp │ ├── reverse-string-ii.cpp │ ├── reverse-string.cpp │ ├── reverse-words-in-a-string.cpp │ ├── ti-huan-kong-ge-lcof.cpp │ └── zuo-xuan-zhuan-zi-fu-chuan-lcof.cpp ├── 6.DoublePointer │ ├── 3sum.cpp │ ├── 4sum.cpp │ ├── intersection-of-two-linked-lists-lcci.cpp │ ├── linked-list-cycle-ii.cpp │ ├── remove-element.cpp │ ├── remove-nth-node-from-end-of-list.cpp │ ├── reverse-linked-list.cpp │ ├── reverse-string.cpp │ └── reverse-words-in-a-string.cpp ├── 7.StackQueue │ ├── evaluate-reverse-polish-notation.cpp │ ├── implement-queue-using-stacks.cpp │ ├── implement-stack-using-queues.cpp │ ├── remove-all-adjacent-duplicates-in-string.cpp │ ├── sliding-window-maximum.cpp │ ├── top-k-frequent-elements.cpp │ ├── valid-parentheses.cpp │ └── 栈与队列.md ├── 8.BinaryTree │ ├── binary-tree-paths.cpp │ ├── find-bottom-left-tree-value.cpp │ ├── lowest-common-ancestor-of-a-binary-tree.cpp │ ├── maximum-binary-tree.cpp │ ├── merge-two-binary-trees.cpp │ ├── path-sum.cpp │ ├── sum-of-left-leaves.cpp │ ├── 二叉树.md │ ├── 二叉树深度 │ │ ├── inimum-depth-of-binary-tree.cpp │ │ ├── maximum-depth-of-binary-tree.cpp │ │ └── maximum-depth-of-n-ary-tree.cpp │ ├── 前中后序遍历 │ │ ├── binary-tree-inorder-traversal.cpp │ │ ├── binary-tree-postorder-traversal.cpp │ │ ├── binary-tree-preorder-traversal.cpp │ │ └── uccessor-lcci.cpp │ ├── 堆结构 │ │ └── 堆结构.md │ ├── 完全二叉树 │ │ └── count-complete-tree-nodes.cpp │ ├── 层序遍历 │ │ ├── average-of-levels-in-binary-tree.cpp │ │ ├── binary-tree-level-order-traversal-ii.cpp │ │ ├── binary-tree-level-order-traversal.cpp │ │ └── binary-tree-right-side-view.cpp │ ├── 平衡二叉树 │ │ └── balanced-binary-tree.cpp │ ├── 搜索二叉树 │ │ ├── delete-node-in-a-bst.cpp │ │ ├── find-mode-in-binary-search-tree.cpp │ │ ├── insert-into-a-binary-search-tree.cpp │ │ ├── lowest-common-ancestor-of-a-binary-search-tree.cpp │ │ ├── minimum-absolute-difference-in-bst.cpp │ │ ├── search-in-a-binary-search-tree.cpp │ │ ├── trim-a-binary-search-tree │ │ ├── trim-a-binary-search-tree.cpp │ │ └── validate-binary-search-tree.cpp │ ├── 构造二叉树 │ │ ├── construct-binary-tree-from-inorder-and-postorder-traversal.cpp │ │ └── construct-binary-tree-from-preorder-and-inorder-traversal.cpp │ └── 翻转二叉树 │ │ ├── invert-binary-tree.cpp │ │ └── symmetric-tree.cpp ├── 9.BacktrackingAlgorithm │ ├── combination-sum-ii.cpp │ ├── combination-sum-iii.cpp │ ├── combination-sum.cpp │ ├── combinations.cpp │ ├── letter-combinations-of-a-phone-number.cpp │ ├── non-decreasing-subsequences.cpp │ ├── palindrome-partitioning.cpp │ ├── permutations-ii.cpp │ ├── permutations.cpp │ ├── restore-ip-addresses.cpp │ ├── subsets-ii.cpp │ ├── subsets.cpp │ ├── sudoku-solver.cpp │ └── 回溯算法.md └── 总结.md └── 面试问题 └── 面试问题.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | *.exe 3 | abab.txt 4 | abab.cpp 5 | MyCoder/Coding/* -------------------------------------------------------------------------------- /ML/机器学习.md: -------------------------------------------------------------------------------- 1 | > 记录下看到的一些学习过程吧 2 | 3 | 4 | # 个人理解 5 | > 单位的模型具体形式已知,但是模型具体参数未知,整体的的模型未知。通过设计损失函数在训练中完善模型的参数。 6 | > 本质还是优化问题,不过有的优化问题是已知完整模型参数,每次优化下降逼近最终结果。ML的训练则是求解模型参数,每次优化下降逼近正确的模型,最后在测试集上实现模型的正确求解最终问题。 -------------------------------------------------------------------------------- /ML/正则化.md: -------------------------------------------------------------------------------- 1 | # 正则化 2 | 3 | $$ 4 | F = \arg\min_{F}Obj(F)=\arg\min_{F}(L(F)+\gamma\Omega(F)) 5 | $$ 6 | 7 | - $Obj(F)$目标函数 8 | - $L(F)$为训练集上损失函数 9 | - $\Omega(F)$是正则项 10 | - $\gamma$控正则项权重。 11 | 12 | ## 目的or作用 13 | 1. 解决适定性问题或过拟合加入额外信息 14 | 2. 将经验风险最小化问题转换为结构风险最小化。 15 | 16 | 17 | ## 常用的正则项 18 | 19 | ### 范数 20 | 希望不要出现过多冗余的参数导致过拟合,就是希望一些单位模型参数等于0,为0点单位模型参数不起作用就OK了。 21 | 22 | ### 香农熵 23 | 不确定高的熵大,用来惩罚不确定性高的估计结果。不确定性高,近似于分类中出现分类概率很大的重叠,熵高概率重叠大分类效果不好。 24 | > 感觉上是,这两个单位模型参数如果得出的结果十分相近,这两个参数效果不好而且没必要。 25 | 26 | ### Total Variation 全变分 27 | 如果图像细节有很多高频信息(如尖刺、噪点等),那么整幅图像的梯度幅值之和(全变分)是比较大的,如果能够使整幅图像的梯度积分之和降低,就达到了去噪的目的。 -------------------------------------------------------------------------------- /ML/网络模型.md: -------------------------------------------------------------------------------- 1 | # 多层感知器 Multilayer Perceptron MLP 2 | 3 | 多层全连接 -------------------------------------------------------------------------------- /MyCoder/ACM/2024/2024秋招.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2hanhan/algorithm/abe1ace7446ea6184b68e0e401ba940af7e194c8/MyCoder/ACM/2024/2024秋招.md -------------------------------------------------------------------------------- /MyCoder/ACM/2024/JD-1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | int main() 5 | { 6 | int n; 7 | cin >> n; 8 | vector nums(n); 9 | for (int i = 0; i < n; i++) 10 | { 11 | cin >> nums[i]; 12 | } 13 | if (n == 0) 14 | { 15 | for (int i = 0; i < 10; i++) 16 | { 17 | cout << 0 << " "; 18 | } 19 | return 0; 20 | } 21 | 22 | vector dp(10, 0); 23 | dp[nums[n - 1]] = 1; 24 | for (int i = n - 1; i > 0; i--) 25 | { 26 | vector temp(10, 0); 27 | for (int j = 0; j <= 9; j++) 28 | { 29 | int x, y; 30 | if (dp[j] == 0) 31 | { 32 | continue; 33 | } 34 | else 35 | { 36 | x = j; 37 | y = nums[i - 1]; 38 | temp[(x * y) % 10] += dp[j]; 39 | temp[(x + y) % 10] += dp[j]; 40 | } 41 | } 42 | dp = temp; 43 | } 44 | 45 | // 输出方案数 46 | for (int i = 0; i < 10; i++) 47 | { 48 | cout << dp[i] << " "; 49 | } 50 | cout << endl; 51 | 52 | return 0; 53 | } -------------------------------------------------------------------------------- /MyCoder/ACM/2024/MT-01.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | vector> g; // 图 5 | vector w; // 节点权值 6 | vector red; // 标记染红的节点 7 | vector visited; // 标记已访问的节点 8 | 9 | bool isSquare(int num) 10 | { 11 | int r = sqrt(num); 12 | return r * r == num; 13 | } 14 | 15 | int dfs(int node) 16 | { 17 | visited[node] = true; 18 | int count = 0; 19 | 20 | // 先访问所有子节点 21 | for (int v : g[node]) 22 | { 23 | if (!visited[v]) 24 | { 25 | count += dfs(v); 26 | } 27 | } 28 | 29 | // 然后尝试染红当前节点和它的所有邻居 30 | for (int v : g[node]) 31 | { 32 | if (!red[node] && !red[v] && isSquare((long long)w[node] * w[v])) 33 | { 34 | red[node] = red[v] = true; 35 | count += 2; 36 | } 37 | } 38 | 39 | return count; 40 | } 41 | 42 | int main() 43 | { 44 | int n, a, b; 45 | cin >> n; 46 | 47 | g.resize(n + 1); 48 | w.resize(n + 1); 49 | red.resize(n + 1, false); 50 | visited.resize(n + 1, false); 51 | 52 | for (int i = 1; i <= n; i++) 53 | { 54 | cin >> w[i]; 55 | } 56 | 57 | for (int i = 0; i < n - 1; i++) 58 | { 59 | cin >> a >> b; 60 | g[a].push_back(b); 61 | g[b].push_back(a); 62 | } 63 | 64 | cout << dfs(1) << endl; 65 | 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /MyCoder/ACM/2024/ZY-1.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2hanhan/algorithm/abe1ace7446ea6184b68e0e401ba940af7e194c8/MyCoder/ACM/2024/ZY-1.cpp -------------------------------------------------------------------------------- /MyCoder/ACM/KamaCoder/网址.md: -------------------------------------------------------------------------------- 1 | [https://kamacoder.com/](https://kamacoder.com/) -------------------------------------------------------------------------------- /MyCoder/ACM/acm.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | // int main() 6 | // { 7 | // string s; 8 | // vector> nums_map; 9 | // while (getline(cin, s)) 10 | // { 11 | // if (s == "") 12 | // break; 13 | // cout << "s:" << s << endl; 14 | // stringstream ss(s); 15 | // int num; 16 | // vector nums; 17 | // while (ss >> num) 18 | // { 19 | // nums.push_back(num); 20 | // } 21 | // nums_map.push_back(nums); 22 | // } 23 | // cout << "结束输入" << endl; 24 | // return 0; 25 | // } 26 | 27 | int main() 28 | { 29 | vector> nums_map; 30 | int length; 31 | while (cin >> length) 32 | { 33 | if (length == 0) 34 | break; 35 | vector nums(length); 36 | for (int i = 0; i < length; i++) 37 | { 38 | cin >> nums[i]; 39 | } 40 | nums_map.push_back(nums); 41 | } 42 | cout << "结束输入" << endl; 43 | return 0; 44 | } -------------------------------------------------------------------------------- /MyCoder/Zuo/01/01_01.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 01_01.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-08-14 7 | * 一根长度为K的绳子,最多能盖住几个点 8 | 给定一个有序数组arr,从左到右依次表示X轴上从左往右点的位置 9 | 给定一个正整数K,返回如果有一根长度为K的绳子,最多能盖住几个点 10 | 绳子的边缘点碰到X轴上的点,也算盖住 11 | * @copyright Copyright (c) 2023 12 | * 13 | */ 14 | #include 15 | using namespace std; 16 | 17 | // 滑窗 18 | int solution(vector nums, int length) 19 | { 20 | int left = 0, right = 0; 21 | int n = nums.size(); 22 | int max_length = 0; 23 | while (left < n) 24 | { 25 | while (right < n && nums[right] - nums[left] <= length) 26 | { 27 | right++; 28 | } 29 | max_length = max(max_length, (right - left++)); 30 | } 31 | return max_length; 32 | } 33 | 34 | int main() 35 | { 36 | 37 | string s; 38 | vector nums; 39 | int num; 40 | getline(cin, s); 41 | stringstream ss(s); 42 | while (ss >> num) 43 | { 44 | nums.push_back(num); 45 | } 46 | 47 | int length; 48 | cin >> length; 49 | 50 | 51 | cout << "数组:"; 52 | for (int i = 0; i < nums.size(); i++) 53 | { 54 | cout << nums[i] << " "; 55 | } 56 | cout << endl; 57 | cout << "绳子长度:" << length << endl; 58 | 59 | cout << "覆盖数目:" << solution(nums, length); 60 | 61 | return 0; 62 | } -------------------------------------------------------------------------------- /MyCoder/Zuo/01/01_04.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 01_04.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-08-14 7 | * 相邻字符的交换次数 8 | 一个数组中只有两种字符'G'和'B’,可以让所有的G都放在左侧, 9 | 所有的B都放在右侧, 或者可以让所有的G都放在右侧,所有的B都放在左侧, 10 | 但是只能在相邻字符之间进行交换操作,返回至少需要交换几次 11 | * @copyright Copyright (c) 2023 12 | * 13 | */ 14 | #include 15 | using namespace std; 16 | 17 | //贪心 18 | int solution(const string &s) 19 | { 20 | int G_count = 0, B_count = 0; 21 | int G_result = 0, B_result = 0; 22 | for (int i = 0; i < s.size(); i++) 23 | { 24 | if (s[i] == 'G') 25 | { 26 | G_result += i - G_count; 27 | G_count++; 28 | } 29 | else 30 | { 31 | B_result += i - B_count; 32 | B_count++; 33 | } 34 | } 35 | 36 | return min(G_result, B_result); 37 | } 38 | 39 | int main() 40 | { 41 | cout<<"输入字符串:"<> s; 44 | cout << "s:" << s << endl; 45 | 46 | cout << "最小交换次数:" << solution(s) << endl; 47 | } -------------------------------------------------------------------------------- /MyCoder/Zuo/01/01_07.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 01_07.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-08-14 7 | * 494.目标和 8 | * https://leetcode.cn/problems/target-sum/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ -------------------------------------------------------------------------------- /MyCoder/Zuo/02/02_04.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 02_04.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-08-14 7 | * 司机最高总收入 8 | 现有司机N人,调度中心会将所有司机平分给A、B两个区域第 i 个司机去A可得收入为income[i][0], 9 | 第 i 个司机去B可得收入为income[i][1], 10 | 返回所有调度方案中能使所有司机总收入最高的方案,是多少钱 11 | * @copyright Copyright (c) 2023 12 | * 13 | */ 14 | 15 | #include 16 | using namespace std; 17 | 18 | /** 19 | * @brief 递归 20 | * 21 | * @param income 22 | * @param index 分配完第几个司机 23 | * @param rest A区域剩余的名额 24 | * @return int 最大收入 25 | */ 26 | int process(const vector> &income, const int &N, int index, int rest) 27 | { 28 | // base case 29 | if (index == N) 30 | return 0; 31 | 32 | if (N - index == rest) 33 | { 34 | return income[index][0] + process(income, N, index + 1, rest - 1); 35 | } 36 | if (rest == 0) 37 | { 38 | return income[index][1] + process(income, N, index + 1, rest); 39 | } 40 | 41 | int result1 = income[index][0] + process(income, N, index + 1, rest - 1); 42 | int result2 = income[index][1] + process(income, N, index + 1, rest); 43 | 44 | return max(result1, result2); 45 | } 46 | 47 | int solution(const vector> &income, int N) 48 | { 49 | if (N % 2 != 0) 50 | return 0; 51 | 52 | return process(income, N, 0, N / 2); 53 | } 54 | 55 | int main() 56 | { 57 | int N; 58 | cin >> N; 59 | 60 | vector> income(N, vector(2)); 61 | for (int i; i < N; i++) 62 | { 63 | cin >> income[i][0] >> income[i][1]; 64 | } 65 | 66 | cout << "result:" << solution(income, N) << endl; 67 | return 0; 68 | } -------------------------------------------------------------------------------- /MyCoder/Zuo/02/02_05.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 02_05.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-08-18 7 | * 数据结构设计之O(1)实现setAll 8 | * setAll(value)将hash表格中所有的元素设置为value 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | unordered_map> mymap; 20 | 21 | int current_time; 22 | int set_all_time; 23 | int all; 24 | 25 | Solution() : current_time(0) 26 | { 27 | } 28 | 29 | void insert(int key, int value) 30 | { 31 | mymap[key] = make_pair(value, current_time++); 32 | } 33 | 34 | void set_all(int value) 35 | { 36 | set_all_time = current_time; 37 | all = value; 38 | } 39 | 40 | int get(int key) 41 | { 42 | if (mymap.find(key) != mymap.end()) 43 | { 44 | if (mymap[key].second < set_all_time) 45 | { 46 | return mymap[key].first; 47 | } 48 | else 49 | { 50 | return all; 51 | } 52 | } 53 | } 54 | }; 55 | 56 | int main() 57 | { 58 | Solution solution; 59 | 60 | return 0; 61 | } -------------------------------------------------------------------------------- /MyCoder/Zuo/03/03_01.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 03_01.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-08-18 7 | * 3. 无重复字符的最长子串 8 | * https://leetcode.cn/problems/longest-substring-without-repeating-characters/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | 14 | #include 15 | using namespace std; 16 | 17 | class Solution 18 | { 19 | public: 20 | }; 21 | 22 | int main() 23 | { 24 | Solution solution; 25 | 26 | 27 | return 0; 28 | } -------------------------------------------------------------------------------- /MyCoder/Zuo/03/03_04.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 03_04.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-08-18 7 | * 最多可以同时有多少场比赛 8 | 给定一个数组arr,代表每个人的能力值。 9 | 再给定一个非负数k如果两个人能力差值正好为k, 10 | 那么可以凑在一起比赛一局比赛只有两个人, 11 | 返回最多可以同时有多少场比赛 12 | * @copyright Copyright (c) 2023 13 | * 14 | */ 15 | 16 | #include 17 | using namespace std; 18 | 19 | // 贪心 20 | class Solution 21 | { 22 | public: 23 | int get_max_num(vector &people, int k) 24 | { 25 | int size = people.size(); 26 | if (size <= 1) 27 | return 0; 28 | 29 | sort(people.begin(), people.end(), less()); // 先排序 30 | 31 | int result = 0; 32 | int left = 0, right = 0; 33 | bool usedR[size]; 34 | memset(usedR, false, sizeof(usedR)); 35 | 36 | while (left < size && right < size) // 不能超过范围 37 | { 38 | if (usedR[left]) // 用过了下一个 39 | { 40 | left++; 41 | } 42 | else if (left >= right) 43 | { 44 | right++; // 窗口中只剩余一个 45 | } 46 | else 47 | { 48 | if (right - left == k) // 能比赛 49 | { 50 | result++; 51 | usedR[right++] = true; // 用过了 52 | left++; // 用过了 53 | } 54 | else if (right - left < k) 55 | { 56 | right++; 57 | } 58 | else 59 | { 60 | left++; 61 | } 62 | } 63 | } 64 | return result; 65 | } 66 | }; 67 | 68 | int main() 69 | { 70 | Solution solution; 71 | 72 | return 0; 73 | } -------------------------------------------------------------------------------- /MyCoder/Zuo/03/03_05.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 03_05.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-08-18 7 | 乘客坐船使用的最少船数 8 | 给定一个数组arr,长度为N且每个值都是正数,代表N个人的体重。再给定一个正数 limit,代表一艘船的载重。 以下是坐船规则, 9 | 1)每艘船最多只能做两人; 10 | 2)乘客 的体重和不能超过limit 11 | 返回如果同时让这N个人过河最少需要几条船。 12 | * @copyright Copyright (c) 2023 13 | * 14 | */ 15 | 16 | // 贪心 17 | // 从体重小于limit/2的数组位置进行分块, 18 | // 左边的任意2人都能一艘船,右边的需要找左边的拼船, 19 | // 右边的从最轻的开始接收拼船,但是要尽可能选左边最重的,把左边更轻的留给右边更重的 20 | // 尽可能的去消除右边的体重大于limit/2 21 | // 左边先用完则,右边剩下的单独乘船,剩下的2人一个船 22 | // 右边的先用完,都是2人一个船,单蹦的另外算+1 -------------------------------------------------------------------------------- /MyCoder/Zuo/04/04_02.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 04_02.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-08-18 7 | * 53. 最大子数组和 8 | * https://leetcode.cn/problems/maximum-subarray/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ -------------------------------------------------------------------------------- /MyCoder/Zuo/04/04_05.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 04_05.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-08-18 7 | * 135.分发糖果 8 | * https://leetcode.cn/problems/candy/ 9 | 原问题: 10 | 老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。你需要按照以下要求,帮助老师给这些孩子分发糖果: 11 | 每个孩子至少分配到 1 个糖果。 12 | 评分更高的孩子必须比他两侧的邻位孩子获得更多的糖果。 13 | 那么这样下来,老师至少需要准备多少颗糖果呢? 14 | 进阶问题: 15 | 在原问题的基础上,增加一个原则: 16 | 相邻的孩子间如果分数一样,分的糖果数必须一样, 返回至少需要分多少糖 17 | * @copyright Copyright (c) 2023 18 | * 19 | */ 20 | 21 | // 原问题: 22 | // 贪心 23 | //使用辅助数组,分别从左到右,从右到左遍历 24 | // 从左到右,递增则多一个糖,相等或者减少则只给一个糖 25 | // 从右到左,递增则多一个糖,相等或者减少则只给一个糖 26 | // 遍历辅助数组,取两个中更大的那个 27 | 28 | // 进阶问题: 29 | // 贪心 30 | //使用辅助数组,分别从左到右,从右到左遍历 31 | // 从左到右,递增则多一个糖,相等则发相等的糖,减少则只给一个糖 32 | // 从右到左,递增则多一个糖,相等则发相等的糖,减少则只给一个糖 33 | // 遍历辅助数组,取两个中更大的那个 34 | -------------------------------------------------------------------------------- /MyCoder/Zuo/05/05_02.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 05_02.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-08-18 7 | 二叉树上的相等子树 8 | 如果一个节点X,它左树结构和右树结构完全一样 9 | 那么我们说以X为头的子树是相等子树 10 | 给定一棵二叉树的头节点head 11 | 返回head整棵树上有多少棵相等子树 12 | * @copyright Copyright (c) 2023 13 | * 14 | */ 15 | 16 | // 递归 17 | // 左右子树返回其相对子树的个数,返回各自树的结构,序列化表示null返回某个字符,前序+中序,或者中序+后序 -------------------------------------------------------------------------------- /MyCoder/Zuo/05/05_03.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 05_03.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-08-18 7 | * 72. 编辑距离 8 | * https://leetcode.cn/problems/edit-distance/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ -------------------------------------------------------------------------------- /MyCoder/Zuo/08/08_02.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 08_02.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-08-19 7 | * 8 | * @copyright Copyright (c) 2023 9 | * 10 | */ 11 | 12 | #include 13 | using namespace std; 14 | 15 | // 双指针 16 | class Solution 17 | { 18 | public: 19 | int maxArea(vector &height) 20 | { 21 | 22 | int result = 0; 23 | int L = 0, R = height.size() - 1; 24 | while (L < R) 25 | { 26 | // 从两边往中间收缩 27 | // 每次移动比较矮的那个,有可能使得存储量比上一次有所增加 28 | if (height[L] > height[R]) 29 | { 30 | int cur = (R - L) * height[R]; 31 | result = result > cur ? result : cur; 32 | R--; 33 | } 34 | else 35 | { 36 | int cur = (R - L) * height[L]; 37 | result = result > cur ? result : cur; 38 | L++; 39 | } 40 | } 41 | return result; 42 | } 43 | }; 44 | 45 | int main() 46 | { 47 | Solution solution; 48 | vector height = {1, 8, 6, 2, 5, 4, 8, 3, 7}; 49 | 50 | cout << solution.maxArea(height) << endl; 51 | 52 | return 0; 53 | } -------------------------------------------------------------------------------- /MyCoder/Zuo/09/09_03.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 09_03.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-08-21 7 | * 300. 最长递增子序列 8 | * https://leetcode.cn/problems/longest-increasing-subsequence/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ -------------------------------------------------------------------------------- /MyCoder/Zuo/09/09_05.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 09_05.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-08-21 7 | * y = f(x) 8 | * f(x) = x + f(x/10) ,x >0 9 | * f(x) = 0 ,x =0 10 | * 已知y求x, 11 | * y 在 [0,x]单调递增, 同时 y>=x ,所以x可能区间是[0,y] 12 | * 单调性,二分查找 [0,y] 13 | * @copyright Copyright (c) 2023 14 | * 15 | */ 16 | 17 | #include 18 | using namespace std; 19 | 20 | class Solution 21 | { 22 | public: 23 | int fun(int x) 24 | { 25 | // base case 26 | if (x == 0) 27 | { 28 | return 0; 29 | } 30 | 31 | return x + fun(x / 10); 32 | } 33 | 34 | int get_x(int y) 35 | { 36 | int left = 0; 37 | int right = y; 38 | while (left <= right) 39 | { 40 | int mid = left + (right - left) / 2; 41 | int result = fun(mid); 42 | if (result == y) 43 | { 44 | return mid; 45 | } 46 | else if (result < y) 47 | { 48 | left = mid + 1; 49 | } 50 | else 51 | { 52 | 53 | right = mid - 1; 54 | } 55 | } 56 | return -1; 57 | } 58 | }; 59 | 60 | int main() 61 | { 62 | Solution solution; 63 | int x = 10; 64 | int y = solution.fun(x); 65 | cout << "x=" << x << "y =" << y << "解得 x =" << solution.get_x(y) << endl; 66 | 67 | for (int i = 0; i < 1000; i++) 68 | { 69 | cout << "y =" << i << "解得 x =" << solution.get_x(i) << endl; 70 | } 71 | 72 | return 0; 73 | } -------------------------------------------------------------------------------- /MyCoder/Zuo/10/10_01.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 10_01.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-08-21 7 | * 45. 跳跃游戏 II 8 | * https://leetcode.cn/problems/jump-game-ii/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ -------------------------------------------------------------------------------- /MyCoder/Zuo/22/22_02.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 22_02.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-09-12 7 | * 42. 接雨水 8 | * https://leetcode.cn/problems/trapping-rain-water/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | using namespace std; 14 | 15 | class Solution 16 | { 17 | public: 18 | int trap(vector &height) 19 | { 20 | int n = height.size(); 21 | vector capacity(n); // 每个位置的蓄水量 22 | 23 | int l = 0; 24 | int heigt_l = height[l]; 25 | int r = n - 1; 26 | int heigt_r = height[r]; 27 | 28 | int sum = 0; 29 | while (l < r) 30 | { 31 | 32 | if (heigt_l <= heigt_r)//每次结算高度更小的一边的水量 33 | { 34 | sum += max(0, heigt_l - height[l++]); 35 | heigt_l = max(heigt_l, height[l]); 36 | } 37 | else 38 | { 39 | sum += max(0, heigt_r - height[r--]); 40 | heigt_r = max(heigt_r, height[r]); 41 | } 42 | } 43 | return sum; 44 | } 45 | }; -------------------------------------------------------------------------------- /MyCoder/Zuo/23/23_04.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 23_04.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-09-12 7 | * 169. 多数元素 8 | * https://leetcode.cn/problems/majority-element/description/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | // 17 | // 时间复杂度O(N) 额外空间复杂度O(1) 18 | class Solution 19 | { 20 | public: 21 | int majorityElement(vector &nums) 22 | { 23 | int count = 0; // 统计数组不同的消除后还剩几个 24 | int value = 0; // 统计数值 25 | for (int i = 0; i < nums.size(); i++) 26 | { 27 | if (count == 0) 28 | { 29 | value = nums[i]; 30 | count++; 31 | } 32 | else 33 | { 34 | if (value == nums[i]) 35 | { 36 | count++; 37 | } 38 | else 39 | { 40 | count--; 41 | } 42 | } 43 | } 44 | 45 | if (count == 0) 46 | return -1; 47 | 48 | count = 0; 49 | for (int i = 0; i < nums.size(); i++) 50 | { 51 | if (value == nums[i]) 52 | count++; 53 | } 54 | if (count > nums.size() / 2) 55 | return value; 56 | else 57 | return -1; 58 | } 59 | }; -------------------------------------------------------------------------------- /MyCoder/Zuo/24/24_06.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 24_06.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-09-20 7 | * 316. 去除重复字母 8 | * https://leetcode.cn/problems/remove-duplicate-letters/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | // 贪心算法 17 | class Solution 18 | { 19 | public: 20 | string removeDuplicateLetters(string s) 21 | { 22 | vector vis(26), num(26); 23 | // 统计每个字符出现的次数 24 | for (char ch : s) 25 | { 26 | num[ch - 'a']++; 27 | } 28 | 29 | string stk; // 存储当前无重复的字符串 30 | for (char ch : s) 31 | { 32 | if (!vis[ch - 'a']) // 之后还有对应的字符 33 | { 34 | while (!stk.empty() && stk.back() > ch) // 栈顶部的字符字典序大于当前字符 35 | { 36 | if (num[stk.back() - 'a'] > 0) // 之后栈顶字符还会出现,当前的元素清除掉,下次用到在添加回来 37 | { 38 | vis[stk.back() - 'a'] = 0; 39 | stk.pop_back(); 40 | } 41 | else // 之后栈顶字符不会再出现,只能保留此元素 42 | { 43 | break; 44 | } 45 | } 46 | vis[ch - 'a'] = 1; // 已经统计过的字符 47 | stk.push_back(ch); 48 | } 49 | num[ch - 'a'] -= 1; // 计数减一 50 | } 51 | return stk; 52 | } 53 | }; -------------------------------------------------------------------------------- /MyCoder/Zuo/26/20_01.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 20_01.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-10-16 7 | * 632. 最小区间 8 | * https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; -------------------------------------------------------------------------------- /MyCoder/Zuo/27/27_02.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 27_02.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-10-16 7 | * 781. 森林中的兔子 8 | * https://leetcode.cn/problems/rabbits-in-forest/description/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | int numRabbits(vector &answers) 20 | { 21 | unordered_map rabits_count; 22 | for (int i = 0; i < answers.size(); i++) 23 | { 24 | rabits_count[answers[i]]++; // 统计回答每个次数有多少个 25 | } 26 | 27 | int sum = 0; 28 | for (auto rabit : rabits_count) 29 | { 30 | int count = rabit.first + 1; // 该颜色个数应该是回答数量+1 31 | int group = rabit.second / count; // 回答相同的凑成一组 32 | group = rabit.second % count == 0 ? group : (group + 1); // 凑组后有剩余的单独成组 33 | sum += group * count; 34 | } 35 | return sum; 36 | } 37 | }; 38 | 39 | int main() 40 | { 41 | // 开始计时 42 | auto start = std::chrono::high_resolution_clock::now(); 43 | 44 | // 代码 45 | Solution solution; 46 | vector answers = {1, 1, 2}; 47 | auto result = solution.numRabbits(answers); 48 | cout << "result:" << result << endl; 49 | 50 | // 结束计时 51 | auto end = std::chrono::high_resolution_clock::now(); 52 | 53 | // 计算耗时 54 | std::chrono::duration duration = end - start; 55 | double seconds = 1000 * duration.count(); 56 | 57 | // 输出结果 58 | std::cout << endl; 59 | std::cout << "Elapsed time: " << std::fixed << std::setprecision(2) << seconds << " ms\n"; 60 | 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /MyCoder/Zuo/29/29_02.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 29_02.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-10-16 7 | * 50. Pow(x, n) 8 | * https://leetcode.cn/problems/powx-n/description/ 9 | * 官方题解沙雕一样,n给定int,他使用long long取巧 10 | * @copyright Copyright (c) 2023 11 | * 12 | */ 13 | 14 | #include 15 | using namespace std; 16 | 17 | // 贪心 时间复杂度 logN 18 | class Solution 19 | { 20 | public: 21 | double myPow(double x, int n) 22 | { 23 | bool positive = true; 24 | bool min_negative = false; 25 | if (n == INT_MIN) 26 | { 27 | n = INT_MAX; 28 | min_negative = true; 29 | positive = false; 30 | } 31 | else if (n < 0) 32 | { 33 | n = -n; 34 | positive = false; 35 | } 36 | 37 | double result = 1; 38 | double cur = x; 39 | 40 | while (n != 0) 41 | { 42 | if ((n & 1) != 0) // 取出2进制最低1位,如果不为0,则需要*x^(二进制的第几位) 43 | { 44 | result *= cur; 45 | } 46 | cur *= cur; 47 | n >>= 1; 48 | } 49 | result = min_negative ? result * x : result; 50 | return positive ? result : 1.0 / result; 51 | } 52 | }; 53 | 54 | int main() 55 | { 56 | // 开始计时 57 | auto start = std::chrono::high_resolution_clock::now(); 58 | 59 | // 代码 60 | Solution solution; 61 | double x = 2.0; 62 | int n = 10; 63 | auto result = solution.myPow(x, n); 64 | cout << "result:" << result << endl; 65 | 66 | // 结束计时 67 | auto end = std::chrono::high_resolution_clock::now(); 68 | 69 | // 计算耗时 70 | std::chrono::duration duration = end - start; 71 | double seconds = 1000 * duration.count(); 72 | 73 | // 输出结果 74 | std::cout << endl; 75 | std::cout << "Elapsed time: " << std::fixed << std::setprecision(2) << seconds << " ms\n"; 76 | 77 | return 0; 78 | } 79 | -------------------------------------------------------------------------------- /MyCoder/Zuo/29/29_06.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 29_06.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-10-16 7 | * 69. x 的平方根 8 | * https://leetcode.cn/problems/sqrtx/description/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | // 高斯牛顿法 测试了一下最少20次迭代能通过所有测试案例 17 | class Solution 18 | { 19 | public: 20 | int mySqrt(int x) 21 | { 22 | double a = 1; // 随便给个初值 23 | // f = a^2-x 24 | // J = 2a,H = 4a^2 25 | // Hdx = -Jf 26 | // dx = -a/2 +x/(2*a) 27 | for (int i = 0; i < 20; i++) 28 | { 29 | a += -a / 2 + x / (2 * a); 30 | } 31 | return a; 32 | } 33 | }; 34 | 35 | int main() 36 | { 37 | // 开始计时 38 | auto start = std::chrono::high_resolution_clock::now(); 39 | 40 | // 代码 41 | Solution solution; 42 | double x = 2.0; 43 | auto result = solution.mySqrt(x); 44 | cout << "result:" << result << endl; 45 | 46 | // 结束计时 47 | auto end = std::chrono::high_resolution_clock::now(); 48 | 49 | // 计算耗时 50 | std::chrono::duration duration = end - start; 51 | double seconds = 1000 * duration.count(); 52 | 53 | // 输出结果 54 | std::cout << endl; 55 | std::cout << "Elapsed time: " << std::fixed << std::setprecision(2) << seconds << " ms\n"; 56 | 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /MyCoder/Zuo/29/29_07.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 29_07.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-10-16 7 | * 73. 矩阵置零 8 | * https://leetcode.cn/problems/set-matrix-zeroes/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | void setZeroes(vector> &matrix) 20 | { 21 | bool set_zero = false; 22 | for (int i = 0; i < matrix.size(); i++) 23 | { 24 | for (int j = 0; j < matrix[0].size(); j++) 25 | { 26 | if (matrix[i][j] == 0) 27 | { 28 | if (j == 0) 29 | { 30 | set_zero = true; 31 | } 32 | else 33 | { 34 | matrix[0][j] = 0; // 列需要置零 35 | } 36 | matrix[i][0] = 0; // 行需要置零 37 | } 38 | } 39 | } 40 | 41 | // 置零,从最后一行开始,因为从第一行开始会导致后续列的置零出错 42 | for (int i = matrix.size() - 1; i >= 0; i--) 43 | { 44 | for (int j = 1; j < matrix[0].size(); j++) 45 | { 46 | if (matrix[0][j] == 0 || matrix[i][0] == 0) 47 | { 48 | matrix[i][j] = 0; 49 | } 50 | } 51 | } 52 | 53 | if (set_zero) 54 | { 55 | for (int i = 0; i < matrix.size(); i++) 56 | { 57 | matrix[i][0] = 0; 58 | } 59 | } 60 | } 61 | }; -------------------------------------------------------------------------------- /MyCoder/Zuo/题目编号.md: -------------------------------------------------------------------------------- 1 | - [参考题目顺序](https://mubu.com/doc/IodJW7cCNk?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE2OTIwMDE0OTIsImZpbGVHVUlEIjoiNjhjZDZoM1R3eWdQd3g4VyIsImlhdCI6MTY5MjAwMTE5MiwiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjotODE4NzgxMTcxOX0.gq7mcmSo3nR20Ybr2WILm4e4MJMl-9Y-enV-pQP_aPk) 2 | 3 | - [刷题JAVA代码实现](https://github.com/algorithmzuo/coding-for-great-offer/tree/main/src) 4 | 5 | > 这里按照[视频](https://www.bilibili.com/video/BV1UB4y1S7dF/?p=1)有的代码题目编号,曾经写过的leetcode题目就直接给出对应的编号,搜索题目编号就可以找到对应的代码和注释。 6 | 7 | -------------------------------------------------------------------------------- /PCL/滤波采样算法.md: -------------------------------------------------------------------------------- 1 | # 最远点采样 Farthest Point Sampling (FPS) 2 | 3 | ## 目的or作用or应用场景 4 | 最远点采样 (FPS, Farthest Point Sampling) 用于在N个点的点云上均匀地采样M个点,使这些点可以比较好地表征点云的整体轮廓。 5 | 6 | ## 算法基本原理 7 | 在$N$个点的点云上迭代的选取$M$个点。每次选取与当前已选取点集合$S = {P_0,P_1,...,P_N}$ 中所有点的最小距离最大的点,加入集合$S^*$。 8 | - 时间复杂度$O(NM)$。 9 | 10 | 11 | # 移动最小二乘 Moving Least Squares (MLS) 12 | ## 目的or作用or应用场景 13 | 创建缺失的部分通过周围数据之间的高阶多项式插值来形成表面点。通过执行重采样,可以纠正这些小错误,并且将多个扫描注册在一起而产生的“双墙”伪影可以被平滑。 14 | 15 | # 增采样 16 | -------------------------------------------------------------------------------- /SLAM/Eigen/Test-Matrix.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | using namespace std; 7 | 8 | int main() 9 | { 10 | Eigen::Matrix3d A; 11 | A << 1, 1, 1, 12 | 0, 5, 0, 13 | 0, 0, 1; 14 | cout << "A.norm():" << A.norm(); // 矩阵的模,各个元素平方和开根号 15 | return 0; 16 | } -------------------------------------------------------------------------------- /SLAM/参考.md: -------------------------------------------------------------------------------- 1 | 2 | # SLAM前端里程计 3 | - [SIFT、SUFT、ORB描述特征](https://zhuanlan.zhihu.com/p/36382429) 4 | - [2D-3D匹配PnP](https://zhuanlan.zhihu.com/p/399140251) 5 | 6 | # SLAM后端优化 7 | - [SLAM中的边缘化](https://xiaotaoguo.com/p/slam-marginalization/) 8 | - [SLAM中的边缘化](https://blog.csdn.net/Hansry/article/details/104412753) 9 | 10 | # 多视图几何 11 | - [多视图几何](https://zhuanlan.zhihu.com/p/143164736) 12 | 13 | 14 | # 面试问题 15 | - [视觉SLAM面试题汇总(2023年春招题库参考)——第一部分](https://zhuanlan.zhihu.com/p/205008396) 16 | 17 | 18 | ## 有待进一步了解的问题 19 | ### 图像 20 | - [ ] 边缘检测算子有哪些? 21 | - [ ] cv::Mat 22 | - [ ] ORB中特征提取的均匀化策略 23 | ### 一些概念 24 | - [ ] RANSAC迭代剔除外点原理? 25 | - [ ] 鲁棒估计方法有哪些? 26 | - [ ] 什么是紧耦合、松耦合?优缺点。 27 | - [ ] 如果对于一个3D点,我们在连续帧之间形成了2D特征点之间的匹配,但是这个匹配中可能存在错误的匹配。请问你如何去构建3D点? 28 | ### 优化 29 | - [ ] 说一下Dog-Leg算法 30 | ### 滤波 31 | - [ ] 推导一下卡尔曼滤波、描述下粒子滤波? 32 | - [ ] EKF和BA的区别? 33 | ### 激光 34 | - [ ] 激光运动畸变 35 | ### IMU 36 | - [ ] IMU的积分方法 37 | ### 计算H、E/F 38 | - [ ] 计算H矩阵和F矩阵的时候有什么技巧呢? 39 | 40 | -------------------------------------------------------------------------------- /cpp/code/test_callback.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | void fun1(int x, int y) 5 | { 6 | cout << "fun1: x:" << x << " y:" << y << endl; 7 | } 8 | 9 | void fun2(int x, int y) 10 | { 11 | cout << "fun2: x:" << x << " y:" << y << endl; 12 | } 13 | 14 | void funx(int x, int y, void(*fun_ptr)(int,int)) 15 | { 16 | fun_ptr(x, y); // 调用回调函数 17 | } 18 | 19 | int main() 20 | { 21 | funx(9, 9, fun1); 22 | funx(9, 9, fun2); 23 | 24 | return 0; 25 | } -------------------------------------------------------------------------------- /cpp/code/test_const.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | // 修饰基本数据类型 6 | void test01() 7 | { 8 | const int a = 0; 9 | int const b = 0; 10 | // const int c;//! 报错 11 | // a = 10; //! 报错 12 | // int *c = &a; //! 报错 13 | } 14 | 15 | // 常量指针 16 | void test02() 17 | { 18 | int a = 0; 19 | int const b = 0; 20 | const int *c = &a; 21 | int const *d = &b; 22 | const int *e; 23 | e = &a; 24 | // *c = 10; //! 报错 25 | a = 10; 26 | // *d = 10; //! 报错 27 | int f = 1; 28 | c = &f; 29 | // int *g = &b; //! 报错 30 | } 31 | 32 | // 指针常量 33 | void test03() 34 | { 35 | int a = 0; 36 | int const b = 0; 37 | int *const c = &a; 38 | // int *const d; //! 报错 39 | // int *const e = &b;//! 报错 40 | int f = 0; 41 | *c = f; 42 | // c = &f;//! 报错 43 | } 44 | 45 | // 指针常量 46 | void test04() 47 | { 48 | int const a = 0; 49 | int b = 0; 50 | int const *const c = &a; 51 | const int *const d = &b; 52 | } 53 | 54 | void test05() 55 | { 56 | int a = 0; 57 | const int b = 0; 58 | const int &c = a; 59 | const int &d = b; 60 | // const int &e; //! 报错 61 | // c = 10;//! 报错 62 | } 63 | 64 | class A 65 | { 66 | public: 67 | A(int _value_a) : value_a(_value_a) 68 | { 69 | } 70 | const int value_a; 71 | int value_b; 72 | void change_value_const(int &value_c) const 73 | { 74 | // value_b = 10;//! 报错 75 | value_c = value_b; 76 | } 77 | void change_value(int &value_c) 78 | { 79 | value_b = 10; 80 | value_c = value_b; 81 | } 82 | }; 83 | 84 | // 修饰类对象 85 | void test06() 86 | { 87 | const A b(10); 88 | int d = 100; 89 | b.change_value_const(d); 90 | // b.change_value(d);//! 报错 91 | } 92 | 93 | int main() 94 | { 95 | 96 | return 0; 97 | } -------------------------------------------------------------------------------- /cpp/code/test_static.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | class A 6 | { 7 | static int value_a; 8 | int value_b; 9 | static int get_value() 10 | { 11 | // value_b = 1; //! 报错 12 | return value_a; 13 | } 14 | }; 15 | 16 | int A::value_a = 0; 17 | 18 | int main() 19 | { 20 | return 0; 21 | } -------------------------------------------------------------------------------- /cpp/code/test_type.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file test_type.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-04-28 7 | * 8 | * @copyright Copyright (c) 2023 9 | * 10 | */ 11 | 12 | #include 13 | using namespace std; 14 | 15 | struct XY 16 | { 17 | int x; 18 | int y; 19 | }; 20 | 21 | void test_size() 22 | { 23 | XY xy_s; 24 | cout << "struct :" << sizeof(xy_s) << endl; 25 | 26 | int xy[2]; 27 | cout << "xy[2]:" << 2 * sizeof(int) << endl; 28 | 29 | vector xy_v={1,2}; 30 | xy_v.reserve(10000000); 31 | // xy_v.resize(10000000); 32 | 33 | cout << "xy_v:" << sizeof(xy_v) << endl; 34 | 35 | 36 | char a[] = "123456789"; 37 | char b[128]; 38 | char *c = a; 39 | cout << "a:" << sizeof(a) << endl; 40 | cout << "b:" << sizeof(b) << endl; 41 | cout << "c:" << sizeof(c) << endl; 42 | 43 | } 44 | 45 | int main(int argc, char **argv) 46 | { 47 | // int arr[2][3] = {00, 01, 02, 10, 11, 12}; 48 | // cout << arr[1][1] << endl; 49 | // arr[0][0] = 100; 50 | // cout << arr[0][0] << endl; 51 | 52 | // int arr1[2][3] = {{00, 01, 02} ,{10, 11, 12}}; 53 | // cout << arr1[1][1] << endl; 54 | // arr1[0][0] = 100; 55 | // cout << arr1[0][0] << endl; 56 | 57 | // int arr2[2][3]; 58 | // cout << arr2[1][1] << endl; 59 | // memset(arr2,-1,sizeof(arr2)); 60 | // cout << arr2[1][1] << endl; 61 | 62 | // int arr3[2][3]; 63 | // cout << arr3[1][1] << endl; 64 | 65 | // cout << sizeof(arr3) << endl; 66 | // cout << sizeof(int) << endl; 67 | 68 | // fill(arr3[0], arr3[0] + sizeof(arr3)/sizeof(int), 990); 69 | // cout << arr3[1][1] << endl; 70 | 71 | // { 72 | // float x = -0.02; 73 | // float size = 0.2; 74 | // int64_t index = x / size; 75 | // cout << index << endl; 76 | // cout << endl; 77 | // } 78 | 79 | test_size(); 80 | 81 | return 0; 82 | } -------------------------------------------------------------------------------- /cpp/一些函数.md: -------------------------------------------------------------------------------- 1 | 2 | # 填充数组 3 | ```c++ 4 | #include 5 | int m = 6, n = 5; 6 | int distance[m][n]; 7 | memset(distance, -1, m * n * sizeof(int)); 8 | ``` 9 | 10 | 11 | 12 | # 数据类型转换 13 | 14 | ```c++ 15 | int n 16 | string s = to_string(n) 17 | 18 | string s = "100"; 19 | int n = stoi(s); 20 | 21 | string s = "100"; 22 | long long n = stoll(s); 23 | 24 | char c = 'a'; 25 | bool is = isalnum(c);//判断c是否是字母或者数子 26 | c = tolower(c);//如果是字母转换为小写字母 27 | ``` 28 | 29 | # 最大值 30 | ```c++ 31 | #include 32 | INT_MAX;//int最大值 33 | INT_MIN; 34 | ``` 35 | 36 | 37 | # 比较器 38 | ```c++ 39 | sort(nums.begin(),nums.end(),greater()); 40 | sort(nums.begin(),nums.end(),less()); 41 | less//从小到大排序 42 | greater//从大到小排序 43 | ``` -------------------------------------------------------------------------------- /cpp/内存管理.md: -------------------------------------------------------------------------------- 1 | # 内存分区 2 | ## 栈 3 | - 编译器管理分配和回收 4 | - 存储局部变量、函数参数 5 | ## 堆 6 | - 程序员管理,`new delete malloc free`进行分配和回收 7 | ## 全局/静态存储区 8 | - 初始化区:存储初始化的全局变量、静态变量 9 | - 未初始化区:存储初始化的全局变量、静态变量 10 | ## 常量存储区 11 | - 存储常量,不允许修改 12 | ## 代码区 13 | - 存储程序的二进制代码 14 | 15 | # new delete malloc free区别 16 | malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。 17 | ## new delete 18 | - 运算符,编译器操作来执行构造和析构 19 | - 按照数据类型申请和释放内存 20 | ## malloc free 21 | - 库函数,编译器无法对其操作来进行构造和析构 22 | - 按照字节长度申请和释放内存 23 | 24 | # 内存泄漏 25 | ## 原因 26 | 程序员申请的空间,使用完后没有释放。 27 | ## 检测 28 | - 观测swap 29 | - /usr/bin/stat工具`netstat`、`vmstat` 30 | - 内存调试,内存泄漏检查以及性能分析软件`valgrind` 31 | 32 | # 内存对齐 33 | - 偏移量必须是min(#pragma pack())的倍数 34 | - 内存对齐可以加速计算 35 | - 某些设备仅支持内存对齐的数据 36 | -------------------------------------------------------------------------------- /cpp/函数.md: -------------------------------------------------------------------------------- 1 | # 函数 2 | 3 | # 形参传递 4 | ## 值传递 5 | ## 指针传递 6 | ## 引用传递 -------------------------------------------------------------------------------- /cpp/函数指针和回调函数.md: -------------------------------------------------------------------------------- 1 | ## 函数指针与回调函数 2 | 具体的[代码实现](./code/test_callback.cpp) 3 | ```c++ 4 | #include 5 | using namespace std; 6 | 7 | void fun1(int x, int y) 8 | { 9 | cout << "fun1: x:" << x << " y:" << y << endl; 10 | } 11 | 12 | void fun2(int x, int y) 13 | { 14 | cout << "fun2: x:" << x << " y:" << y << endl; 15 | } 16 | 17 | void funx(int x, int y, void(*fun_ptr)(int,int)) 18 | { 19 | fun_ptr(x, y); // 调用回调函数 20 | } 21 | 22 | 23 | 24 | int main() 25 | { 26 | funx(9, 9, fun1); 27 | funx(9, 9, fun2); 28 | 29 | return 0; 30 | } 31 | ``` 32 | ## 函数指针 33 | ```c++ 34 | // 定义一个函数指针 35 | void (*fun_ptr)(int, int); 36 | 37 | // 调用函数指针 38 | fun_ptr(0,1); 39 | ``` 40 | 41 | ## 回调函数 42 | 通过形参是函数指针的函数调用具体的函数实现。函数指针指向的函数被称为回调函数。 43 | ### 注意事项 44 | - 回调函数应该是全局or静态函数 45 | - 尽量不要在类内定义回调函数。 46 | > 个人感觉是因为类内的函数是为了方便访问类内成员。但是`static`修饰的静态成员函数,只能访问`static`修饰的静态成员变量,这就失去了定义类内成员函数的初衷了。 47 | ### 使用建议 48 | - 为了访问类内的函数,可以将类的指针传递给回调函数。 49 | -------------------------------------------------------------------------------- /cpp/宏.md: -------------------------------------------------------------------------------- 1 | # 宏 2 | 属于预编译阶段 3 | ## define 4 | ```c++ 5 | #define value_x 1000 6 | ``` 7 | 相当于对文本进行替换 -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 算法学习与刷题面试 2 | ## 目录 3 | - 算法学习:根据代码随想录结合zuo进行学习与刷题的总结 4 | - SLAM: SLAM算法个人总结 5 | - cpp:八股相关还有一些特性验证测试 6 | - MyCoder: 刷题记录 7 | 8 | 9 | 10 | ## 参考学习资料 11 | ### 文档类型 12 | 1. [排序算法](https://www.runoob.com/w3cnote_genre/algorithm) 13 | 这玩意的可视化过程很形象。 14 | 2. [代码随想录](https://programmercarl.com/) 15 | ### 视频类型 16 | 1. [《代码随想录》](https://space.bilibili.com/525438321/channel/collectiondetail?sid=180037) 17 | 18 | 2. [LeetCode左程云视频](https://www.bilibili.com/video/BV1UB4y1S7dF/?p=1) 19 | - [算法课help info](https://shimo.im/docs/68cd6h3TwygPwx8W/read) 20 | - [算法学习代码](https://github.com/algorithmzuo/algorithmbasic2020/tree/master) 21 | - [刷题JAVA代码实现](https://github.com/algorithmzuo/coding-for-great-offer/tree/main/src) 22 | - [一些题目](https://mubu.com/doc/IodJW7cCNk?accessToken=eyJhbGciOiJIUzI1NiIsImtpZCI6ImRlZmF1bHQiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE2OTIwMDE0OTIsImZpbGVHVUlEIjoiNjhjZDZoM1R3eWdQd3g4VyIsImlhdCI6MTY5MjAwMTE5MiwiaXNzIjoidXBsb2FkZXJfYWNjZXNzX3Jlc291cmNlIiwidXNlcklkIjotODE4NzgxMTcxOX0.gq7mcmSo3nR20Ybr2WILm4e4MJMl-9Y-enV-pQP_aPk) 23 | > 这里面的对于暴力递归总结的很彻底。 24 | 25 | 3. [左程云bilibili主页](https://space.bilibili.com/8888480/video) 26 | - [对应代码链接](https://github.com/algorithmzuo/algorithm-journey/tree/main) 27 | # VSCode 28 | Windows11、mingw64编译器:中文路径无法运行改成英文文件夹 29 | 30 | -------------------------------------------------------------------------------- /test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2hanhan/algorithm/abe1ace7446ea6184b68e0e401ba940af7e194c8/test -------------------------------------------------------------------------------- /test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | int main(int argc, char **argv) 7 | { 8 | cout << "Hello world!" << endl; 9 | 10 | string line = "1704681348.994629 lidar"; 11 | std::stringstream iss(line); 12 | 13 | double d; 14 | string s; 15 | if (iss >> d >> s) 16 | { 17 | 18 | cout << "d:" << d << " s:" << s << endl; 19 | } 20 | else 21 | { 22 | cout << line << endl; 23 | } 24 | 25 | return 0; 26 | } -------------------------------------------------------------------------------- /数学概念/各种距离.md: -------------------------------------------------------------------------------- 1 | 2 | # 马氏距离 Mahalanobis Distance [参考](https://zhuanlan.zhihu.com/p/46626607) 3 | 4 | ## 公式 5 | - 单个点的:$D_M(x) = \sqrt{(x-\mu)\Sigma^{-1}(x-\mu)}$ 6 | - 数据点之间的马氏距离:$D_M(x,y) = \sqrt{(x-y)\Sigma^{-1}(x-y)}$ 7 | 8 | ## 应用 9 | - 欧氏距离的一种修正,修正了欧式距离中各个维度尺度不一致且相关的问题。 10 | 11 | # 豪斯多夫距离Hausdorff distance [参考](https://zhuanlan.zhihu.com/p/351921396) 12 | 13 | ## 公式 14 | - 双向Hausdorff距离:$H(A,B) = \max(h(A,B),h(B,A))$ 15 | - 点集A到点集B的单向Hausdorff距离:$h(A,B)=\max_{a\in A}\{\min_{b\in B}||a-b||\}$ 16 | -------------------------------------------------------------------------------- /数学概念/概率.md: -------------------------------------------------------------------------------- 1 | # 概率 2 | ## 贝叶斯公式 3 | - $P(x|y)=\frac{P(y|x)P(x)}{P(y)}$ 4 | ## 极大似然估计 Maximize Likelihood Estimation MLE 5 | - 模型确定,参数未知 6 | - 求解$\theta$使得$P(x_0|\theta)$最大 7 | ## 最大后验估计 Maximum A Posteriori MAP 8 | - 模型确定,参数未知 9 | - 求解$\theta$使得$P(\theta|x_0)=\frac{P(x_0|\theta)P(\theta)}{P(x_0)}$最大 10 | - P(x_0)是可以根据观测获得的固定值,所有转换为求解$P(x_0|\theta)P(\theta)$最大 11 | -------------------------------------------------------------------------------- /数学概念/流形.md: -------------------------------------------------------------------------------- 1 | # 流行 [参考](https://zh.wikipedia.org/wiki/%E6%B5%81%E5%BD%A2) 2 | > 个人感受就是拥有很好的**邻域性质**的空间。可以通过同胚的空间进行降维计算。 3 | 4 | ## 坐标卡 5 | 一个流形的一个坐标映射(coordinate map),坐标卡(coordinate chart),或简称卡是一个在流形的一个开子集和一个简单空间之间的双射,使得该映射及其逆都保持所要的结构。 6 | 7 | - 应用: 8 | 1. 使得计算可以在简单空间进行,再把结果传回流形。 9 | 10 | ## 图册 11 | 多数流形的表述需要多于一个的卡(只有最简单的流形只用一个卡)。覆盖流形的一个特定的卡的集合称为一个图册(atlas)。 12 | 13 | - 特点: 14 | 1. 不唯一 -------------------------------------------------------------------------------- /数学概念/滤波.md: -------------------------------------------------------------------------------- 1 | # 滤波 TODO 2 | - >个人感觉滤波的方法,都是基于状态转移(运动方差),和观测方程进行的推到,重点是先把这两个方程推到出来,然后带入预测和更新公式就好。 3 | ## 卡尔曼滤波器 KF 4 | ### 已知 5 | - 先验、状态转移矩阵(包含状态转移误差$\omega_k $的协方差$R$)$x_k = Ax_{k-1} + u_k + \omega_k $ 6 | - 观测方程(包含观测误差$ v_k$的协方差$Q$)$z_k = Cx_k + v_k$ 7 | - 上一个状态量的先验$x_{k-1}$ 8 | 9 | ### 预测 10 | - 先验状态$x_k = Ax_{k-1}+u_k$ 11 | - 先验协方差$P_k=AP_{k-1}A^T+R_k$ 12 | ### 更新 13 | - 卡尔曼增益$K_k = P_kC^T(CP_kC^T+R)^{-1}$ 14 | - 后验状态$x_k^* = x_k + K_k(z_k-Cx_k)$ 15 | - 后验协方差$P_k^* = (I-K_kC)P_k$ 16 | 17 | ### 求解与推到[参考讲解](https://www.bilibili.com/video/BV1ez4y1X7eR/?spm_id_from=333.999.0.0&vd_source=d2698384821931f16375af13c5b44a9f) 18 | - 当前状态时刻的后验$x_k^*$,使得$error = x_{k_{true}} - x_k^*$最小 19 | - 等价与使得error的协方差最小 20 | - 结果是当卡尔曼增益$K_k = P_kC^T(CP_kC^T+R)^{-1}$时误差最小 21 | - 先验协方差$P_k = AP_{k-1}^*A^T+Q$是先验状态估计和真实值的误差$error = x_{k_{true}}-x_k = Ae_{k-1}+\omega_{k-1}$的协方差。 22 | ## 扩展卡尔曼滤波 EKF 23 | ### 特点 24 | - EKF通过在均值附近对非线性方程一阶泰勒展开,通过获取非线性函数在均值的斜率来构造近似线性函数。也就是$A,B,C$是不固定的。 25 | - 误差状态总是在原点0附近,远离了奇异值、万向节锁等问题,从而确保卡尔曼滤波时候的合理性和有效性。 26 | 27 | ## 误差状态卡尔曼滤波 ESKF 28 | - 所有的状态均使用误差状态来表达 29 | ### 已知 30 | - 先验、状态转移矩阵(包含状态转移误差$\omega$的协方差$R$)$\delta x = F\delta x + \omega $ 31 | - 观测方程(包含观测误差$ v$的协方差$Q$)$z = Hx + v$ 32 | - 上一个状态量的先验$x_{k-1}$ 33 | 34 | ### 预测 35 | - 先验状态$\delta x_{pre} = F\delta x$ 36 | - 先验协方差$P_{pre} = FPF^T+R$ 37 | ### 更新 38 | - 卡尔曼增益$K = P_{pre}H^T(HP_{pre}H^T+Q)^{-1}$ 39 | - 后验状态$\delta x = K(z-Hx)$ 40 | - 后验状态$x = x_{pre} + \delta x$ 41 | - 后验协方差$P = (I-KH)P_{PRE}$ 42 | ### 对比KF优点 43 | - 使用三维变量表达旋转,没有冗余和万向锁问题。 44 | - 总在原点附近,离奇异点远,数值更稳定,线性化近似效果更好。 45 | - 状态量为小量,二阶变量可忽略。 46 | - 状态转移方程,运动学方程比原状态变量更小。 47 | -------------------------------------------------------------------------------- /算法学习/1. Algorithm/help/help.md: -------------------------------------------------------------------------------- 1 | # 帮助数组 2 | ## 前缀和数组 3 | 4 | ### 应用与题目 5 | 6 | - 求数组以i位置结尾的,和为sum的最长的子数组,sum位置等于【help[x]-sum】这个前缀和第一次出现的位置。 7 | - [未排序数组中累加和为给定值的最长子数组系列问](https://www.nowcoder.com/practice/545544c060804eceaed0bb84fcd992fb) 8 | - [1124. 表现良好的最长时间段](https://leetcode.cn/problems/longest-well-performing-interval/) 9 | - [1590. 使数组和能被 P 整除](https://leetcode.cn/problems/make-sum-divisible-by-p/) 10 | - [1371. 每个元音包含偶数次的最长子字符串 11 | ](https://leetcode.cn/problems/find-the-longest-substring-containing-vowels-in-even-counts/) 12 | - 求数组以i位置结尾的,和为sum的子数组个数,sum位置等于【help[x]-sum】这个前缀和出现的次数 13 | - [560. 和为 K 的子数组](https://leetcode.cn/problems/subarray-sum-equals-k/) 14 | - [303. 区域和检索 - 数组不可变](https://leetcode.cn/problems/range-sum-query-immutable/) 15 | 16 | ## Tire前缀树[讲解](https://www.bilibili.com/video/BV1Yu4y1Q7vR/?spm_id_from=333.999.0.0&vd_source=d2698384821931f16375af13c5b44a9f) 17 | 18 | ### 实现 19 | 每个节点统计出现次数pass以及结尾end出现次数 20 | 头结点开始,下一个value不存在则新建节点pass++,若是结尾end++ 21 | 删除string,经过依次pass--,pass为0则需要delete 22 | 23 | 可以使用静态数组的方式,减少测试所有用例的总的内存占用,每次用完后手动归零静态数组。 24 | 25 | 节点如果分叉过大可以分开用表示,比如`abc`可以转换会`a->b->c` 26 | 27 | ### 应用 28 | 统计前缀出现次数 29 | 30 | - [LCR 067. 数组中两个数的最大异或值](https://leetcode.cn/problems/ms70jA/) 31 | - y因为可以转换为32位二进制 32 | -------------------------------------------------------------------------------- /算法学习/1. Algorithm/help/前缀和数组/PositivesEqualsNegtivesLongestSubarray.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file PositivesEqualsNegtivesLongestSubarray.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-09-04 7 | * 未排序数组中累加和为给定值的最长子数组系列问题补1 8 | * https://www.nowcoder.com/practice/545544c060804eceaed0bb84fcd992fb 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | int main() 17 | { 18 | int n; 19 | cin >> n; 20 | 21 | vector nums(n); 22 | 23 | for (int i = 0; i < n; i++) 24 | { 25 | cin >> nums[i]; 26 | } 27 | 28 | // 等价与把正数看做1,负数看做-1,0还是0 29 | // 然后求sum等于0的最长子数组长度 30 | 31 | unordered_map map; // 前缀和等于sum第一次出现位置 32 | map[0] = -1; 33 | 34 | int sum = 0; 35 | int length = 0; 36 | for (int i = 0; i < n; i++) 37 | { 38 | int cur = 0; 39 | if (nums[i] > 0) 40 | { 41 | cur = 1; 42 | } 43 | else if (nums[i] == 0) 44 | { 45 | cur = 0; 46 | } 47 | else 48 | { 49 | cur = -1; 50 | } 51 | sum += cur; 52 | 53 | if (map.find(sum) != map.end()) 54 | { 55 | length = max(length, i - map[sum]); 56 | } 57 | else 58 | { 59 | map[sum] = i; 60 | } 61 | } 62 | 63 | cout << length << endl; 64 | 65 | return 0; 66 | } -------------------------------------------------------------------------------- /算法学习/1. Algorithm/help/前缀和数组/find-the-longest-substring-containing-vowels-in-even-counts.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file find-the-longest-substring-containing-vowels-in-even-counts.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-09-04 7 | * 1371. 每个元音包含偶数次的最长子字符串 8 | * https://leetcode.cn/problems/find-the-longest-substring-containing-vowels-in-even-counts/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | int findTheLongestSubstring(string s) 20 | { 21 | unordered_map map; // 第次层出现的位置 22 | map[0] = -1; 23 | 24 | int max_length = 0; 25 | int count_aeiou = 0; // 16,8,4,2,1位置1代表奇数个,0代表偶数个 26 | for (int i = 0; i < s.size(); i++) 27 | { 28 | count_aeiou = count_aeiou ^ char_to_count(s[i]); // 当前的奇数偶数次数 29 | 30 | if (map.find(count_aeiou) != map.end()) 31 | { 32 | max_length = max(max_length, i - map[count_aeiou]); // 奇偶数次数相等的,一减就都是偶数了 33 | } 34 | else 35 | { 36 | map[count_aeiou] = i; 37 | } 38 | } 39 | return max_length; 40 | } 41 | 42 | int char_to_count(char c) 43 | { 44 | if (c == 'a') 45 | return 1; 46 | else if (c == 'e') 47 | return 2; 48 | else if (c == 'i') 49 | return 4; 50 | else if (c == 'o') 51 | return 8; 52 | else if (c == 'u') 53 | return 16; 54 | else 55 | return 0; 56 | } 57 | }; -------------------------------------------------------------------------------- /算法学习/1. Algorithm/help/前缀和数组/longest-well-performing-interval.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file longest-well-performing-interval.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-09-04 7 | * 1124. 表现良好的最长时间段 8 | * https://leetcode.cn/problems/longest-well-performing-interval/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | int longestWPI(vector &hours) 20 | { 21 | 22 | int max_length = 0; 23 | int sum = 0; 24 | 25 | unordered_map map; 26 | map[0] = -1; 27 | 28 | for (int i = 0; i < hours.size(); i++) 29 | { 30 | int cur = 0; 31 | if (hours[i] <= 8) 32 | { 33 | cur = -1; 34 | } 35 | else 36 | { 37 | cur = 1; 38 | } 39 | 40 | sum += cur; 41 | 42 | if (sum - 1 <= 0) 43 | { 44 | // 如果不超过一半,则需要求和为【sum -1】时的位置index,这样数组[index+1,i]求和刚好能超过一半 45 | if (map.find(sum - 1) != map.end()) 46 | { 47 | max_length = max(max_length, i - map[sum - 1]); 48 | } 49 | } 50 | else 51 | { 52 | max_length = i + 1; // 直接符合大于8小时的超过一半 53 | } 54 | 55 | if (map.find(sum) == map.end()) 56 | { 57 | map[sum] = i; 58 | } 59 | } 60 | 61 | return max_length; 62 | } 63 | }; -------------------------------------------------------------------------------- /算法学习/1. Algorithm/help/前缀和数组/make-sum-divisible-by-p.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file make-sum-divisible-by-p.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-09-04 7 | * 1590. 使数组和能被 P 整除 8 | * https://leetcode.cn/problems/make-sum-divisible-by-p/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | int minSubarray(vector &nums, int p) 20 | { 21 | int mod = 0; // 总的余数 22 | for (int i = 0; i < nums.size(); i++) 23 | { 24 | mod = (mod + nums[i]) % p; 25 | } 26 | 27 | if (mod == 0) 28 | return 0; 29 | 30 | int min_length = nums.size(); 31 | int cur_mod = 0; // 当前的余数 32 | unordered_map map; 33 | map[0] = -1; 34 | 35 | for (int i = 0; i < nums.size(); i++) 36 | { 37 | cur_mod = (cur_mod + nums[i]) % p; 38 | int need_mod = (cur_mod - mod + p) % p; // mod = cur_mod - need_mod 两个玩意的余数应该相等,但是不确定谁大谁小统一加个p 39 | if (map.find(need_mod) != map.end()) 40 | { 41 | min_length = min(min_length, i - map[need_mod]); 42 | } 43 | 44 | map[cur_mod] = i; 45 | } 46 | 47 | return min_length == nums.size() ? -1 : min_length; 48 | } 49 | }; -------------------------------------------------------------------------------- /算法学习/1. Algorithm/help/前缀和数组/range-sum-query-immutable.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file range-sum-query-immutable.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-09-04 7 | * 303. 区域和检索 - 数组不可变 8 | * https://leetcode.cn/problems/range-sum-query-immutable/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | class NumArray 17 | { 18 | public: 19 | vector help; 20 | NumArray(vector &nums) 21 | { 22 | help.resize(nums.size() + 1, 0); // 多一个可以减少边界讨论的情况 23 | 24 | for (int i = 0; i < nums.size(); i++) 25 | { 26 | help[i + 1] = help[i] + nums[i]; 27 | } 28 | } 29 | 30 | int sumRange(int left, int right) 31 | { 32 | return help[right + 1] - help[left]; 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /算法学习/1. Algorithm/help/前缀和数组/subarray-sum-equals-k.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file subarray-sum-equals-k.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-09-04 7 | * 560. 和为 K 的子数组 8 | * https://leetcode.cn/problems/subarray-sum-equals-k/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | 17 | class Solution 18 | { 19 | public: 20 | int subarraySum(vector &nums, int k) 21 | { 22 | unordered_map map; // 前缀和sum出现的次数 23 | map[0] = 1; 24 | 25 | int count = 0; 26 | int sum = 0; 27 | for (int i = 0; i < nums.size(); i++) 28 | { 29 | sum += nums[i]; 30 | count += map[sum - k]; 31 | map[sum]++; 32 | } 33 | return count; 34 | } 35 | }; -------------------------------------------------------------------------------- /算法学习/1. Algorithm/二分算法/RobotPassThroughBuilding.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file RobotPassThroughBuilding.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-09-09 7 | * 机器人跳跃问题 8 | * https://www.nowcoder.com/practice/7037a3d57bbd4336856b8e16a9cafd71 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | bool can(const vector &nums, const int &max, const int &m) 17 | { 18 | int cur = m; 19 | for (int i = 0; i < nums.size(); i++) 20 | { 21 | cur = cur + (cur - nums[i]); 22 | if (cur >= max) 23 | return true; 24 | 25 | if (cur < 0) 26 | return false; 27 | } 28 | return true; 29 | } 30 | 31 | // 二分法 32 | int main() 33 | { 34 | int n; 35 | cin >> n; 36 | vector nums(n); 37 | int max_num = 0; 38 | for (int i = 0; i < n; i++) 39 | { 40 | cin >> nums[i]; 41 | max_num = max(max_num, nums[i]); 42 | } 43 | 44 | int ans = 0; 45 | int l = 0; 46 | int r = max_num; 47 | 48 | while (l <= r) 49 | { 50 | int m = l + ((r - l) >> 2); 51 | if (can(nums, max_num, m)) 52 | { 53 | ans = m; 54 | r = m - 1; 55 | } 56 | else 57 | { 58 | l = m + 1; 59 | } 60 | } 61 | 62 | cout << ans << endl; 63 | return 0; 64 | } -------------------------------------------------------------------------------- /算法学习/1. Algorithm/二分算法/koko-eating-bananas.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file koko-eating-bananas.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-09-09 7 | * 875. 爱吃香蕉的珂珂 8 | * https://leetcode.cn/problems/koko-eating-bananas/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | using namespace std; 14 | 15 | // 二分法 16 | class Solution 17 | { 18 | public: 19 | bool can(const vector &piles, const int &h, const int &speed) 20 | { 21 | int time = 0; 22 | 23 | for (int i = 0; i < piles.size(); i++) 24 | { 25 | time += (piles[i] + speed - 1) / speed; // 此次耗时 26 | if (time > h) 27 | return false; 28 | } 29 | return true; 30 | } 31 | int minEatingSpeed(vector &piles, int h) 32 | { 33 | int l = 1; 34 | int r = 0; 35 | 36 | for (int i = 0; i < piles.size(); i++) 37 | { 38 | r = max(r, piles[i]); 39 | } 40 | 41 | // 在l到r之间二分 42 | int ans = 0; 43 | int m = 0; 44 | while (l <= r) 45 | { 46 | m = l + ((r - l) >> 1); 47 | if (can(piles, h, m)) 48 | { 49 | ans = m; // 记录能符合条件的结果 50 | r = m - 1; 51 | } 52 | else 53 | { 54 | l = m + 1; 55 | } 56 | } 57 | 58 | return ans; 59 | } 60 | }; -------------------------------------------------------------------------------- /算法学习/1. Algorithm/二分算法/maximum-running-time-of-n-computers.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file maximum-running-time-of-n-computers.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-09-09 7 | * 2141. 同时运行 N 台电脑的最长时间 8 | * https://leetcode.cn/problems/maximum-running-time-of-n-computers/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | // 贪心判断能够满足n个电脑运行m分钟 20 | bool can(const vector &batteries, int n, const long long &m) 21 | { 22 | long long sum = 0; 23 | 24 | for (int i = 0; i < batteries.size(); i++) 25 | { 26 | if (batteries[i] >= m) 27 | { 28 | n--; 29 | } 30 | else 31 | { 32 | sum += batteries[i]; 33 | } 34 | } 35 | 36 | return sum >= n * m; 37 | } 38 | 39 | long long maxRunTime(int n, vector &batteries) 40 | { 41 | 42 | long long ans; 43 | long long l = 0; 44 | long long r = 0; 45 | 46 | int max_batterie = 0; 47 | for (int i = 0; i < batteries.size(); i++) 48 | { 49 | r += batteries[i]; 50 | max_batterie = max(max_batterie, batteries[i]); 51 | } 52 | 53 | // 剪枝全是碎片电池 54 | if (r > (long)max_batterie * n) 55 | { 56 | return r / n; 57 | } 58 | 59 | r = max_batterie;//全是碎片电池不能满足,则r的最多范围是max_batterie 60 | 61 | while (l <= r) 62 | { 63 | long long m = l + ((r - l) >> 1); 64 | if (can(batteries, n, m)) 65 | { 66 | l = m + 1; 67 | ans = m; 68 | } 69 | else 70 | { 71 | r = m - 1; 72 | } 73 | } 74 | 75 | return ans; 76 | } 77 | }; -------------------------------------------------------------------------------- /算法学习/1. Algorithm/二分算法/split-array-largest-sum.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file split-array-largest-sum.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-09-09 7 | * 410. 分割数组的最大值 8 | * https://leetcode.cn/problems/split-array-largest-sum/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | // 二分法 17 | class Solution 18 | { 19 | public: 20 | bool can(const vector &nums, const int &k, const int &m) 21 | { 22 | int count = 1; 23 | int sum = 0; 24 | for (int i = 0; i < nums.size(); i++) 25 | { 26 | if (nums[i] > m) 27 | return false; 28 | 29 | sum += nums[i]; 30 | if (sum > m) 31 | { 32 | count++; 33 | sum = nums[i]; 34 | } 35 | if (count > k) 36 | return false; 37 | } 38 | 39 | return true; 40 | } 41 | 42 | int splitArray(vector &nums, int k) 43 | { 44 | int ans = 0; 45 | int l = 0; 46 | int r = 0; 47 | for (int i = 0; i < nums.size(); i++) 48 | { 49 | r += nums[i]; 50 | } 51 | 52 | while (l <= r) 53 | { 54 | int m = l + ((r - l) >> 1); 55 | if (can(nums, k, m)) 56 | { 57 | ans = m; 58 | r = m - 1; 59 | } 60 | else 61 | { 62 | l = m + 1; 63 | } 64 | } 65 | return ans; 66 | } 67 | }; 68 | -------------------------------------------------------------------------------- /算法学习/1. Algorithm/二分算法/二分.md: -------------------------------------------------------------------------------- 1 | # 二分算法 [视频](https://www.bilibili.com/video/BV1Mh4y1P7qE/?spm_id_from=444.41.list.card_archive.click&vd_source=d2698384821931f16375af13c5b44a9f) 2 | 1. 能确定答案的取值范围`[left,right]` 3 | 2. 问题的答案在取值范围内是单调的 4 | 3. 设置一个`fun(int mid)`判断`mid`是否能满足条件 5 | 4. 在取值范围上`[left,right]`进行二分,直至二分结束找到最合适的答案。 6 | -------------------------------------------------------------------------------- /算法学习/1. Algorithm/位运算/按位操作.md: -------------------------------------------------------------------------------- 1 | # 按位操作 2 | ## 或运算 3 | ```c++ 4 | int value1 = 1; 5 | int value2 = 8; 6 | value1|value2 7 | ``` 8 | 9 | ## 与运算 10 | ```c++ 11 | int value1 = 1; 12 | int value2 = 8; 13 | value1&value2 14 | ``` 15 | 16 | ## 异或运算 17 | 可以理解无进位相加,具有交换结合率 18 | ```c++ 19 | a^0 = a 20 | a^a = 0 21 | a^b^c = a^(b^c) = a^c^b //和顺序无关 22 | ``` 23 | ### 应用 24 | 交换两个数字 25 | ```c++ 26 | a = a^b; 27 | b = a^b; 28 | a = a^b; 29 | ``` 30 | 31 | ## 移位运算 32 | 33 | ### 应用 34 | 无符号数除以2等价于右移一位 35 | ```c++ 36 | int middle = left + ((right - left) >> 1); 37 | ``` 38 | 39 | 无符号数称2等价于左移一位 40 | ```c++ 41 | int middle = left + ((right - left) << 1); 42 | ``` 43 | 44 | 乘2+1 <<1再和1或运算 45 | ```c++ 46 | int middle = left + (*(right - left) << 1)|1); 47 | ``` 48 | 49 | ## 取反操作 50 | ``` 51 | int value = 1; 52 | value = ~value; 53 | ``` 54 | 55 | ### 应用 56 | 取最右侧为1的位 57 | ```c++ 58 | int x = x & (~x + 1) 59 | ``` -------------------------------------------------------------------------------- /算法学习/1. Algorithm/坑.md: -------------------------------------------------------------------------------- 1 | # 坑 2 | > 感觉是就是一些无解的问题,或者错误的说法。[LeetCode,算法(左程云)](https://www.bilibili.com/video/BV1Ef4y1T7Qi/?spm_id_from=333.999.0.0&vd_source=d2698384821931f16375af13c5b44a9f) 3 | ## 排序 4 | 1. 归并排序的额外空间复杂度可以变成O(1),"归并排序内部缓存法",但是将变得不再稳定。`不如直接堆排序` 5 | 2. "原地归并排序"是垃圾帖子,会使时间复杂度变成O(N^2)。`不如插入排序` 6 | 3. 快速排序稳定性改进,"01 stable sort"但是对于样本数据要求更多。`对数据状况限制,不应该属于基于比较的排序` 7 | 4. 问题:在整型数组中,把奇数放在左边,把偶数放在右边,要求奇数、偶数之间的相对次序不变。要求时间复杂度O(N),额外空间复杂度O(1)。`这是一个01partion问题并且要求稳定性,快速排序partion过程做不到稳定性` 8 | > 坑4. 这个玩意具体的题目可以使用双指针的方式解决啊。可能是C++方便实现,这个教程主要是Java所以给出这种说法? 9 | 10 | ## 链表 11 | 1. 问题:只给需要删除的节点不给头结点,能否实现删除?`不能。有种讨巧的做法把下一个节点的值复制到该节点,然后删除下一个节点,但是这种做法没办法处理最后一个节点。这种不是属于链表的删除方式` -------------------------------------------------------------------------------- /算法学习/1. Algorithm/数据结构.md: -------------------------------------------------------------------------------- 1 | # 数据结构 2 | - 连续 3 | - 跳转 4 | ## 数组 5 | ## 链表 6 | ## 哈希表 7 | ## 字符串 8 | ## 栈与队列 9 | 10 | ## help 结构 11 | 前缀和数组 -------------------------------------------------------------------------------- /算法学习/1. Algorithm/算法.md: -------------------------------------------------------------------------------- 1 | # 算法 2 | - 知道如何计算出结果 3 | - 知道如何试出结果 4 | 5 | ## 时间复杂度 6 | ### 时间复杂度排序 7 | O(1)常数阶 < O(logn)对数阶 < O(n)线性阶 < O(n^2)平方阶 < O(n^3)立方阶 < O(2^n)指数阶 8 | 9 | ### 常数项时间 10 | 实现的细节,申请大量样本运行比较 11 | 12 | 位运算 < 13 | ## 空间复杂度 14 | 空间复杂度是考虑程序**运行时占用内存的大小**,而不是可执行文件的大小。 15 | 16 | 额外空间复杂度,申请的额外的空间 17 | 18 | 19 | 20 | ## 随机数生成算法 21 | 由任意概率生成器,拼出来等概率的生成器 22 | 1. 先拼个等概率0、1生成器,如果不是有的类似概率归一化操作的反向使用,不是我需要要的情况就舍去重新随机一次。 23 | 2. 然后0、1生成器模仿二进制数生成 $[x-x,y-x)$直接的等概率生成器 24 | 25 | ## [排序算法](./排序算法/排序算法.md) 26 | 27 | 28 | # 内存 29 | ## 内存管理 30 | 区域名称|特点|作用 31 | -|-|- 32 | 代码区|固定部分|存储二进制代码。 33 | 数据区|固定部分|全局变量、静态变量、常量等。 34 | 栈区(Stack) |可变部分|系统自动分配回收。 35 | 堆区(Heap)|可变部分|new出来的,需要手动delete。若程序员不释放,程序结束时可能由OS收回。 36 | 37 | 38 | ## 内存对齐 39 | 占用内存有所增加,可以加速提升计算效率。 40 | 41 | # 数据性能对比 42 | 数据类型|插入/删除|查询|使用场景 43 | -|-|-|- 44 | 数组|O(n)|O(1)|数据量固定,经常查询 45 | 链表|O(1)|O(n)|数据量不固定,频繁增删,较少查询 -------------------------------------------------------------------------------- /算法学习/1. Algorithm/运算操作.md: -------------------------------------------------------------------------------- 1 | # (a/b)向上取整 2 | ```c++ 3 | int a; 4 | int b; 5 | int result = (a+b-1)/b; 6 | ``` 7 | 8 | 9 | 10 | # 交换两个数字 11 | ```c++ 12 | a = a^b; 13 | b = a^b; 14 | a = a^b; 15 | ``` 16 | 17 | # 无符号数除以2等价于右移一位 18 | ```c++ 19 | int middle = left + ((right - left) >> 1); 20 | ``` 21 | 22 | # 取最右侧为1的位 23 | ```c++ 24 | int x = x & (~x + 1) 25 | ``` 26 | 27 | # 无符号数除以2等价于右移一位 28 | ```c++ 29 | int middle = left + ((right - left) >> 1); 30 | ``` 31 | 32 | # 无符号数乘2等价于左移一位 33 | ```c++ 34 | int middle = left + ((right - left) << 1); 35 | ``` 36 | 37 | # 乘2+1 <<1再和1或运算 38 | ```c++ 39 | int middle = left + (*(right - left) << 1)|1); 40 | ``` 41 | -------------------------------------------------------------------------------- /算法学习/1. Algorithm/递归算法/递归算法.md: -------------------------------------------------------------------------------- 1 | # 递归算法 2 | 递归栈,系统递归抽象是一个栈。 3 | ## 递归算法时间复杂度 4 | Master公式 5 | $$ 6 | T\left(N\right) =a* T\left(N/b\right)+O\left(N^d\right) 7 | $$ 8 | $a$是每次递归调用子问题递归次数,$N/b$是子问题递归的规模,$O\left(N^d\right)$是除去递归调用其他操作的时间复杂度。 9 | 1. 如果$log_ba > d$:时间复杂度$O\left(N^{log_ba}\right)$ 10 | 2. 如果$log_ba < d$:时间复杂度$O\left(N^{d}\right)$ 11 | 3. 如果$log_ba == d$:时间复杂度$O\left(N^{d}*logN\right)$ 12 | 13 | 14 | ## 暴力递归算法 15 | > 这玩意是改动态递归的基础 16 | ### 步骤 17 | 1. 把问题分解为缩小规模的子问题 18 | 2. 有明确的不需要继续进行递归的条件(**base case**) 19 | 3. 有当得到了子问题结果之后决策 20 | 4. 不记录每个子问题的解 21 | 22 | 暴力递归存在很多重复运算,可以把这些重复运算结果保存的缓存`cache`中。 23 | 24 | ### 原则 25 | 1. 可变参数类型,不要比`int`复杂 26 | 2. 1.中可以违反,让类型突破到一维线性结构,那必须是唯一可变参数 27 | 3. 1.违反但是2.不违反,只需要记忆化搜索 28 | 4. 可变参数个数尽可能少 29 | 30 | ### 去重 31 | 32 | #### 过滤方法 33 | 获取全部结果之后,再剔除(最简单的使用`set`进行去重) 34 | #### 分支限界 35 | 利用限制提前去除支路 36 | -------------------------------------------------------------------------------- /算法学习/10. GreedyAlgorithm/assign-cookies.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file assign-cookies.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-12 7 | * 455. 分发饼干 8 | * https://leetcode.cn/problems/assign-cookies/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | int findContentChildren(vector &g, vector &s) 20 | { 21 | sort(g.begin(),g.end()); 22 | sort(s.begin(),s.end()); 23 | int p1 = 0; 24 | int p2 = 0; 25 | while (p1 < g.size() && p2 < s.size()) 26 | { 27 | if (s[p2] >= g[p1]) 28 | { 29 | p2++; 30 | p1++; 31 | } 32 | else 33 | p2++; 34 | } 35 | return p1; 36 | } 37 | }; -------------------------------------------------------------------------------- /算法学习/10. GreedyAlgorithm/best-time-to-buy-and-sell-stock-ii.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file best-time-to-buy-and-sell-stock-ii.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-12 7 | * 122. 买卖股票的最佳时机 II 8 | * https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | // 贪心 16 | class Solution 17 | { 18 | public: 19 | int maxProfit(vector &prices) 20 | { 21 | if (prices.size() <= 1) 22 | return 0; 23 | 24 | int result = 0; 25 | int pre = prices[0]; 26 | 27 | for (int i = 1; i < prices.size(); i++) 28 | { 29 | result += prices[i] - pre > 0 ? prices[i] - pre : 0; 30 | pre = prices[i]; 31 | } 32 | return result; 33 | } 34 | }; 35 | 36 | class Solution 37 | { 38 | public: 39 | int maxProfit(vector &prices) 40 | { 41 | int len = prices.size(); 42 | if (len == 0) 43 | return 0; 44 | // dp[i][1] 第i天持有股票 45 | // dp[i][0] 第i天不持有股票 46 | vector> dp(len, vector(2)); 47 | dp[0][1] = -prices[0]; 48 | dp[0][0] = 0; 49 | for (int i = 1; i < len; i++) 50 | { 51 | dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]); 52 | dp[i][0] = max(dp[i - 1][1] + prices[i], dp[i - 1][0]); 53 | } 54 | return dp[len - 1][0]; 55 | } 56 | }; -------------------------------------------------------------------------------- /算法学习/10. GreedyAlgorithm/candy.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file candy.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-13 7 | * 135. 分发糖果 8 | * https://leetcode.cn/problems/candy/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | using namespace std; 14 | class Solution 15 | { 16 | public: 17 | int candy(vector &ratings) 18 | { 19 | vector candys(ratings.size(), 1); 20 | // 从左到右来一遍多的就多个一个,少的就只给一个 21 | for (int i = 1; i < ratings.size(); i++) 22 | { 23 | candys[i] = ratings[i] > ratings[i - 1] ? candys[i - 1] + 1 : 1; 24 | } 25 | // 从右到左来一遍多的就多个一个,少的就维持不变 26 | for (int i = ratings.size() - 2; i >= 0; i--) 27 | { 28 | if (ratings[i] > ratings[i + 1]) 29 | { 30 | // 原本糖数不满足的,重新给糖 31 | if (candys[i] <= candys[i + 1]) 32 | candys[i] = candys[i + 1] + 1; 33 | } 34 | } 35 | // 统计糖总数 36 | int result = 0; 37 | for (int i = 0; i < candys.size(); i++) 38 | { 39 | result += candys[i]; 40 | } 41 | return result; 42 | } 43 | }; -------------------------------------------------------------------------------- /算法学习/10. GreedyAlgorithm/gas-station.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file gas-station.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-13 7 | * 134. 加油站 8 | * https://leetcode.cn/problems/gas-station/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | 14 | using namespace std; 15 | class Solution 16 | { 17 | public: 18 | int canCompleteCircuit(vector &gas, vector &cost) 19 | { 20 | int start = 0; 21 | int sum = 0; 22 | int curSum = 0; 23 | for (int i = 0; i < gas.size(); i++) 24 | { 25 | // 当前的油耗超过了加的油,尝试从下一站开始 26 | curSum += gas[i] - cost[i]; 27 | if (curSum < 0) 28 | { 29 | start = i + 1; 30 | curSum = 0; 31 | } 32 | // 总共的油耗是否大于总共的油,总油够用就一定存在一个可以实现的出发点 33 | sum += gas[i] - cost[i]; 34 | } 35 | if (sum < 0) 36 | return -1; 37 | return start; 38 | } 39 | }; -------------------------------------------------------------------------------- /算法学习/10. GreedyAlgorithm/jump-game.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file jump-game.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-12 7 | * 55. 跳跃游戏 8 | * https://leetcode.cn/problems/jump-game/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | using namespace std; 14 | class Solution 15 | { 16 | public: 17 | bool canJump(vector &nums) 18 | { 19 | int max = 0; 20 | for (int i = 0; i <= max; i++) 21 | { 22 | max = i + nums[i] > max ? i + nums[i] : max; 23 | 24 | if (max >= nums.size() - 1) 25 | return true; 26 | } 27 | return false; 28 | } 29 | }; -------------------------------------------------------------------------------- /算法学习/10. GreedyAlgorithm/lemonade-change.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lemonade-change.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-13 7 | * 860. 柠檬水找零 8 | * https://leetcode.cn/problems/lemonade-change/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | using namespace std; 14 | //优先用大面值找零 15 | class Solution 16 | { 17 | public: 18 | bool lemonadeChange(vector &bills) 19 | { 20 | int doll5 = 0; 21 | int doll10 = 0; 22 | for (int i = 0; i < bills.size(); i++) 23 | { 24 | if (bills[i] == 5) 25 | doll5++; 26 | else if (bills[i] == 10) 27 | { 28 | if (doll5 > 0) 29 | { 30 | doll5--; 31 | doll10++; 32 | } 33 | else 34 | { 35 | return false; 36 | } 37 | } 38 | else 39 | { 40 | if (doll10 > 0) 41 | { 42 | doll10--; 43 | if (doll5 > 0) 44 | doll5--; 45 | else 46 | return false; 47 | } 48 | else 49 | { 50 | if (doll5 >= 3) 51 | doll5 -= 3; 52 | else 53 | return false; 54 | } 55 | } 56 | } 57 | return true; 58 | } 59 | }; -------------------------------------------------------------------------------- /算法学习/10. GreedyAlgorithm/maximize-sum-of-array-after-k-negations.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file maximize-sum-of-array-after-k-negations.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-12 7 | * 1005. K 次取反后最大化的数组和 8 | * https://leetcode.cn/problems/maximize-sum-of-array-after-k-negations/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | #include 14 | using namespace std; 15 | class Solution 16 | { 17 | public: 18 | int largestSumAfterKNegations(vector &nums, int k) 19 | { 20 | //排序 21 | sort(nums.begin(), nums.end()); 22 | // 从最小的负数开始,到正数结束,或者i到头了 23 | int i = 0; 24 | while (k > 0 && i = 0 && nums[i] > nums[i - 1]||i == nums.size()) 42 | nums[i - 1] = -nums[i - 1]; 43 | else 44 | nums[i] = -nums[i]; 45 | } 46 | 47 | //求和 48 | int result = 0; 49 | for (int i = 0; i < nums.size(); i++) 50 | { 51 | result += nums[i]; 52 | } 53 | 54 | return result; 55 | } 56 | }; -------------------------------------------------------------------------------- /算法学习/10. GreedyAlgorithm/maximum-subarray.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file maximum-subarray.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-12 7 | * 53. 最大子数组和 8 | * https://leetcode.cn/problems/maximum-subarray/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | using namespace std; 14 | 15 | // 贪心 16 | class Solution 17 | { 18 | public: 19 | int maxSubArray(vector &nums) 20 | { 21 | int result = nums[0]; 22 | int sum = result; 23 | for (int i = 1; i < nums.size(); i++) 24 | { 25 | if (sum < 0)//如果先前的求和sum为负数,那么加元素nums[i]一定会不如只取nums[i]值大 26 | sum = 0;//所以sum归零,从nums[i]开始重新求和 27 | 28 | sum += nums[i]; 29 | if (sum > result) 30 | result = sum; 31 | } 32 | return result; 33 | } 34 | }; -------------------------------------------------------------------------------- /算法学习/10. GreedyAlgorithm/merge-intervals.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file merge-intervals.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-13 7 | * 56. 合并区间 8 | * https://leetcode.cn/problems/merge-intervals/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | class Solution 18 | { 19 | public: 20 | static bool cmp(const vector &a, const vector &b) 21 | { 22 | if (a[0] == b[0]) 23 | return a[1] < b[1]; 24 | return a[0] < b[0]; 25 | } 26 | 27 | vector> merge(vector> &intervals) 28 | { 29 | vector> result; 30 | if (intervals.size() == 0) 31 | return result; 32 | // 按照左端点排序 33 | sort(intervals.begin(), intervals.end(), cmp); 34 | 35 | int left = intervals[0][0]; 36 | int right = intervals[0][1]; 37 | for (int i = 1; i < intervals.size(); i++) 38 | { 39 | if (right >= intervals[i][0]) 40 | { 41 | right = right > intervals[i][1] ? right : intervals[i][1]; // right取最大的 42 | } 43 | else 44 | { 45 | vector one{left, right}; 46 | result.push_back(one); 47 | // 更新结果 48 | left = intervals[i][0]; 49 | right = intervals[i][1]; 50 | } 51 | } 52 | // 保存最后一组 53 | vector one{left, right}; 54 | result.push_back(one); 55 | 56 | return result; 57 | } 58 | }; -------------------------------------------------------------------------------- /算法学习/10. GreedyAlgorithm/non-overlapping-intervals.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file non-overlapping-intervals.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-13 7 | * 435. 无重叠区间 8 | * https://leetcode.cn/problems/non-overlapping-intervals/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | class Solution 18 | { 19 | public: 20 | static bool cmp(const vector &a, const vector &b) 21 | { 22 | return a[1] < b[1]; 23 | } 24 | int eraseOverlapIntervals(vector> &intervals) 25 | { 26 | if (intervals.size() == 0) 27 | return 0; 28 | 29 | sort(intervals.begin(), intervals.end(), cmp); 30 | int result = 1; // 可以拆分为非重叠区间个数 31 | int right = intervals[0][1]; 32 | for (int i = 1; i < intervals.size(); i++) 33 | { 34 | if (intervals[i][0] >= right)//等于的不算是重叠 35 | { 36 | result++; 37 | right = intervals[i][1]; 38 | } 39 | } 40 | return intervals.size()-result; 41 | } 42 | }; -------------------------------------------------------------------------------- /算法学习/10. GreedyAlgorithm/partition-labels.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file partition-labels.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-13 7 | * 763. 划分字母区间 8 | * https://leetcode.cn/problems/partition-labels/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | using namespace std; 17 | 18 | class Solution 19 | { 20 | public: 21 | vector partitionLabels(string s) 22 | { 23 | 24 | // 统计最远距离 25 | unordered_map map; 26 | for (int i = s.size() - 1; i >= 0; i--) 27 | { 28 | if (map.find(s[i]) == map.end()) 29 | map[s[i]] = i; 30 | } 31 | // 获取结果 32 | vector result; 33 | int left = 0; 34 | int right = 0; 35 | for (int i = 0; i < s.size(); i++) 36 | { 37 | if (right >= i) 38 | right = right > map[s[i]] ? right : map[s[i]]; 39 | else 40 | { 41 | result.push_back(i - left); 42 | left = i; 43 | right = map[s[i]]; 44 | } 45 | } 46 | //统计最后一个区间长度 47 | result.push_back(right + 1 - left); 48 | return result; 49 | } 50 | }; -------------------------------------------------------------------------------- /算法学习/10. GreedyAlgorithm/queue-reconstruction-by-height.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file queue-reconstruction-by-height.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-13 7 | * 406. 根据身高重建队列 8 | * https://leetcode.cn/problems/queue-reconstruction-by-height/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | class Solution 18 | { 19 | public: 20 | static bool cmp(const vector &a, const vector &b) 21 | { 22 | if (a[0] == b[0]) 23 | return a[1] < b[1]; 24 | return a[0] > b[0]; 25 | } 26 | vector> reconstructQueue(vector> &people) 27 | { 28 | // 首先按照身高排序,高的在前面,相同身高的,前面人数少的在前面 29 | sort(people.begin(), people.end(), cmp); 30 | 31 | // 再根据前面有几个人,进行插入 32 | list> que; 33 | for (int i = 0; i < people.size(); i++) 34 | { 35 | int position = people[i][1]; 36 | list>::iterator it = que.begin(); 37 | while (position--) 38 | { 39 | it++; 40 | } 41 | que.insert(it, people[i]); 42 | } 43 | 44 | return vector>(que.begin(), que.end()); 45 | } 46 | }; -------------------------------------------------------------------------------- /算法学习/10. GreedyAlgorithm/wiggle-subsequence.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file wiggle-subsequence.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-12 7 | * 376. 摆动序列 8 | * https://leetcode.cn/problems/wiggle-subsequence/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | using namespace std; 14 | 15 | class Solution 16 | { 17 | public: 18 | int wiggleMaxLength(vector &nums) 19 | { 20 | int result = 0; 21 | int pre = 2; 22 | int now = 0; 23 | for (int i = 0; i < nums.size() - 1; i++) 24 | { 25 | 26 | if (nums[i] > nums[i + 1]) // 导数为正 27 | now = 1; 28 | else if (nums[i] == nums[i + 1]) // 导数为0,取之前的导数 29 | now = pre; 30 | else // 导数为负数 31 | now = -1; 32 | 33 | if (now * pre == -1) // 统计导数过0点 34 | result++; 35 | pre = now; 36 | } 37 | if (pre == 2) // 导数全部为0 38 | return 1; 39 | 40 | return result + 2; //导数不全为0 41 | } 42 | }; -------------------------------------------------------------------------------- /算法学习/10. GreedyAlgorithm/贪心算法.md: -------------------------------------------------------------------------------- 1 | # 贪心算法 2 | 3 | 由局部最优解可以获取全局最优解 4 | > 都说看经验,没有规律套路,证明可能太难了搞数学的都不一定。 -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/01背包问题/last-stone-weight-ii.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file last-stone-weight-ii.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-16 7 | * 1049. 最后一块石头的重量 II 8 | * https://leetcode.cn/problems/last-stone-weight-ii/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | int lastStoneWeightII(vector &stones) 20 | { 21 | 22 | int sum = 0; 23 | for (int i = 0; i < stones.size(); i++) 24 | { 25 | sum += stones[i]; 26 | } 27 | 28 | vector DP((sum >> 1) + 1, 0); 29 | for (int i = 0; i < stones.size(); i++) 30 | { 31 | for (int j = (sum >> 1); j >= stones[i]; j--) 32 | { 33 | DP[j] = max(stones[i] + DP[j - stones[i]], DP[j]); 34 | } 35 | } 36 | 37 | return abs(sum - 2 * DP[sum >> 1]); 38 | } 39 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/01背包问题/partition-equal-subset-sum.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file partition-equal-subset-sum.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-16 7 | * 416. 分割等和子集 8 | * https://leetcode.cn/problems/partition-equal-subset-sum/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | using namespace std; 14 | 15 | class Solution 16 | { 17 | public: 18 | bool canPartition(vector &nums) 19 | { 20 | // 求和 21 | int sum = 0; 22 | for (int i = 0; i < nums.size(); i++) 23 | sum += nums[i]; 24 | if (sum % 2 == 0) 25 | sum = sum >> 1; 26 | else 27 | return false; 28 | 29 | // 初始化 30 | vector DP(sum + 1, 0); 31 | // 滚动式填充一维数组 32 | for (int i = 0; i < nums.size(); i++) 33 | { 34 | //尝试累加nums[i] 35 | for (int j = sum; j >= nums[i]; j--) 36 | { 37 | int cursum1 = nums[i] + DP[j - nums[i]]; 38 | int cursum2 = DP[j]; 39 | DP[j] = cursum1 > cursum2 ? cursum1 : cursum2; 40 | if (DP[j] == sum) 41 | return true; 42 | } 43 | } 44 | return false; 45 | } 46 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/01背包问题/target-sum.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file target-sum.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-16 7 | * 494. 目标和 8 | * https://leetcode.cn/problems/target-sum/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | int findTargetSumWays(vector &nums, int target) 20 | { 21 | 22 | int sum = 0; 23 | for (int i = 0; i < nums.size(); i++) 24 | { 25 | sum += nums[i]; 26 | } 27 | target = target > 0? target : -target; 28 | 29 | // 奇、偶性一致,目标targrt要小于等于sum 30 | if ((sum - target) % 2 == 0 && sum >= target) 31 | sum = (sum + target) >> 1;//P-N=T => P = (S+T)/2 32 | else 33 | return 0; 34 | 35 | vector DP(sum + 1, 0); 36 | DP[0] = 1; 37 | for (int i = 0; i < nums.size(); i++) 38 | { 39 | for (int j = sum; j >= nums[i]; j--) 40 | { 41 | DP[j] += DP[j - nums[i]];//用j位置的数+不用j位置的数凑出来sum 42 | } 43 | } 44 | 45 | return DP[sum]; 46 | } 47 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/Subsequence/delete-operation-for-two-strings.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file delete-operation-for-two-strings.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-18 7 | * 583. 两个字符串的删除操作 8 | * https://leetcode.cn/problems/delete-operation-for-two-strings/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | //DP 18 | class Solution 19 | { 20 | public: 21 | int minDistance(string word1, string word2) 22 | { 23 | int m = word1.size(); 24 | int n = word2.size(); 25 | 26 | vector> dp(m + 1, vector(n + 1, 0)); 27 | // 初始化 28 | for (int i = 0; i < m + 1; i++) 29 | dp[i][0] = i; // 删除i个变成0个 30 | 31 | for (int j = 0; j < n + 1; j++) 32 | dp[0][j] = j; // 删除j个变成0个 33 | for (int i = 1; i < m + 1; i++) 34 | { 35 | for (int j = 1; j < n + 1; j++) 36 | { 37 | if (word1[i - 1] == word2[j - 1]) 38 | dp[i][j] = dp[i - 1][j - 1]; 39 | else 40 | dp[i][j] = min(dp[i - 1][j] + 1, dp[i][j - 1] + 1); 41 | } 42 | } 43 | return dp[m][n]; 44 | } 45 | }; 46 | 47 | // 方法二:先求最长公共子序列、然后word1+word2总长度 - 2*最长公共子序列长度 -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/Subsequence/distinct-subsequences.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file distinct-subsequences.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-18 7 | * 115. 不同的子序列 8 | * https://leetcode.cn/problems/distinct-subsequences/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | #include 14 | using namespace std; 15 | 16 | 17 | class Solution 18 | { 19 | public: 20 | int numDistinct(string s, string t) 21 | { 22 | int m = s.size(); 23 | int n = t.size(); 24 | 25 | vector> dp(m + 1, vector(n + 1, 0)); // 使用到s中的第i个,能凑出了多少个t中到第j个的数组,i、j从1开始到m+1、n+1 26 | 27 | // 初始化 28 | for (int i = 0; i < m + 1; i++) 29 | dp[i][0] = 1; 30 | 31 | for (int i = 1; i < m + 1; i++) 32 | { 33 | for (int j = 1; j < n + 1; j++) 34 | { 35 | if (s[i - 1] == t[j - 1]) 36 | dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]; // 使用s中第i个 + 不使用s中的第i个 37 | else 38 | dp[i][j] = dp[i - 1][j]; // 不使用s中的第i个 39 | } 40 | } 41 | return dp[m][n]; 42 | } 43 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/Subsequence/is-subsequence.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file is-subsequence.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-18 7 | * 392. 判断子序列 8 | * https://leetcode.cn/problems/is-subsequence/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | // DP 18 | class Solution 19 | { 20 | public: 21 | bool isSubsequence(string s, string t) 22 | { 23 | int m = s.size(); 24 | int n = t.size(); 25 | 26 | vector> dp(m + 1, vector(n + 1, 0)); 27 | for (int i = 1; i < m + 1; i++) 28 | { 29 | for (int j = 1; j < n + 1; j++) 30 | { 31 | if (s[i - 1] == t[j - 1]) 32 | dp[i][j] = dp[i - 1][j - 1] + 1; 33 | else 34 | dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); 35 | } 36 | } 37 | return dp[m][n] == s.size(); 38 | } 39 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/Subsequence/longest-common-subsequence.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file longest-common-subsequence.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-18 7 | * 1143. 最长公共子序列 8 | * https://leetcode.cn/problems/longest-common-subsequence/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | int longestCommonSubsequence(string text1, string text2) 20 | { 21 | int m = text1.size(); 22 | int n = text2.size(); 23 | 24 | vector> dp(m, vector(n, 0)); 25 | int result = 0; 26 | // 初始化 27 | int same = 0; 28 | for (int i = 0; i < m; i++) 29 | { 30 | if (text1[i] == text2[0]) 31 | { 32 | same = 1; 33 | } 34 | dp[i][0] = same; 35 | } 36 | same = 0; 37 | for (int j = 0; j < n; j++) 38 | { 39 | if (text1[0] == text2[j]) 40 | { 41 | same = 1; 42 | } 43 | dp[0][j] = same; 44 | } 45 | 46 | for (int i = 1; i < m; i++) 47 | { 48 | for (int j = 1; j < n; j++) 49 | { 50 | if (text1[i] == text2[j]) 51 | dp[i][j] = dp[i - 1][j - 1] + 1; 52 | else 53 | dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]); 54 | } 55 | } 56 | return dp[m - 1][n - 1]; 57 | } 58 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/Subsequence/longest-continuous-increasing-subsequence.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file longest-continuous-increasing-subsequence.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-18 7 | * 674. 最长连续递增序列 8 | * https://leetcode.cn/problems/longest-continuous-increasing-subsequence/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | int findLengthOfLCIS(vector &nums) 20 | { 21 | int size = nums.size(); 22 | if (size <= 1) 23 | return size; 24 | int result = 1; 25 | vector dp(size, 1); 26 | for (int i = 1; i < size; i++) 27 | { 28 | if (nums[i] > nums[i - 1]) 29 | dp[i] = dp[i - 1] + 1; 30 | 31 | if (result < dp[i]) 32 | result = dp[i]; 33 | } 34 | return result; 35 | } 36 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/Subsequence/longest-palindromic-subsequence.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file longest-palindromic-subsequence.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-18 7 | * 516. 最长回文子序列 8 | * https://leetcode.cn/problems/longest-palindromic-subsequence/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | using namespace std; 17 | 18 | class Solution 19 | { 20 | public: 21 | int longestPalindromeSubseq(string s) 22 | { 23 | int size = s.size(); 24 | vector> dp(size, vector(size, 0)); // 从i到j的最长回文子序列长度 25 | 26 | for (int i = size - 1; i >= 0; i--) 27 | { 28 | for (int j = i; j < size; j++) 29 | { 30 | if (i + 1 >= j) 31 | { 32 | if (s[i] == s[j]) 33 | dp[i][j] = j - i + 1; 34 | } 35 | else 36 | { 37 | if (s[i] == s[j]) 38 | dp[i][j] = dp[i + 1][j - 1] + 2; 39 | else 40 | dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]); 41 | } 42 | } 43 | } 44 | return dp[0][size - 1]; 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/Subsequence/maximum-length-of-repeated-subarray.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file maximum-length-of-repeated-subarray.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-18 7 | * 718. 最长重复子数组 8 | * https://leetcode.cn/problems/maximum-length-of-repeated-subarray/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | int findLength(vector &nums1, vector &nums2) 20 | { 21 | int m = nums1.size(); 22 | int n = nums2.size(); 23 | if (m == 0 || n == 0) 24 | return 0; 25 | 26 | vector> dp(m, vector(n, 0)); 27 | 28 | // 初始化 29 | int result = 0; 30 | for (int i = 0; i < m; i++) 31 | { 32 | if (nums1[i] == nums2[0]) 33 | { 34 | dp[i][0] = 1; 35 | result = 1; 36 | } 37 | } 38 | for (int i = 0; i < n; i++) 39 | { 40 | if (nums1[0] == nums2[i]) 41 | { 42 | dp[0][i] = 1; 43 | result = 1; 44 | } 45 | } 46 | 47 | for (int i = 1; i < m; i++) 48 | { 49 | for (int j = 1; j < n; j++) 50 | { 51 | if (nums1[i] == nums2[j]) 52 | dp[i][j] = dp[i - 1][j - 1] + 1; 53 | if (result < dp[i][j]) 54 | result = dp[i][j]; 55 | } 56 | } 57 | return result; 58 | } 59 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/Subsequence/maximum-subarray.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file maximum-subarray.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-18 7 | * 53. 最大子数组和 8 | * https://leetcode.cn/problems/maximum-subarray/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | using namespace std; 14 | 15 | // DP 16 | class Solution 17 | { 18 | public: 19 | int maxSubArray(vector &nums) 20 | { 21 | vector dp(nums.size(), 0); 22 | dp[0] = nums[0]; 23 | int result = dp[0]; 24 | for (int i = 1; i < nums.size(); i++) 25 | { 26 | dp[i] = max(dp[i - 1] + nums[i], nums[i]); 27 | if (result < dp[i]) 28 | result = dp[i]; 29 | } 30 | 31 | return result; 32 | } 33 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/Subsequence/palindromic-substrings.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file palindromic-substrings.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-18 7 | * 647. 回文子串 8 | * https://leetcode.cn/problems/palindromic-substrings/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | using namespace std; 17 | class Solution 18 | { 19 | public: 20 | int countSubstrings(string s) 21 | { 22 | vector> dp(s.size(), vector(s.size(), false)); 23 | 24 | int result = 0; 25 | 26 | for (int i = s.size() - 1; i >= 0; i--) 27 | { 28 | for (int j = i; j < s.size(); j++) 29 | { 30 | if (i + 1 >= j) 31 | dp[i][j] = s[i] == s[j]; 32 | else 33 | dp[i][j] = dp[i + 1][j - 1] && s[i] == s[j]; 34 | 35 | if (dp[i][j]) 36 | result++; 37 | } 38 | } 39 | return result; 40 | } 41 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/Subsequence/uncrossed-lines.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file uncrossed-lines.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-18 7 | * 1035. 不相交的线 8 | * https://leetcode.cn/problems/uncrossed-lines/ 9 | * 本质上是求最长公共子序列长度 10 | * @copyright Copyright (c) 2023 11 | * 12 | */ 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | int maxUncrossedLines(vector &nums1, vector &nums2) 20 | { 21 | int m = nums1.size(); 22 | int n = nums2.size(); 23 | 24 | vector> dp(m + 1, vector(n + 1, 0)); 25 | for (int i = 1; i < m + 1; i++) 26 | { 27 | for (int j = 1; j < n + 1; j++) 28 | { 29 | if (nums1[i-1] == nums2[j-1])//主要nums1、nums2是从0到m、n 30 | dp[i][j] = dp[i - 1][j - 1] + 1; 31 | else 32 | dp[i][j] = max(dp[i - 1][j], dp[i][j-1]); 33 | } 34 | } 35 | return dp[m][n]; 36 | } 37 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/climbing-stairs.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file climbing-stairs.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-15 7 | * 70. 爬楼梯 8 | * https://leetcode.cn/problems/climbing-stairs/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | vector nums; 20 | int climbStairs(int n) 21 | { 22 | if (n <= 0) 23 | return 0; 24 | nums.resize(n + 1, 1); 25 | for (int i = 2; i < n + 1; i++) 26 | { 27 | nums[i] = nums[i - 1] + nums[i - 2]; 28 | } 29 | return nums[n]; 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/fibonacci-number.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file fibonacci-number.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-15 7 | * 509. 斐波那契数 8 | * @copyright Copyright (c) 2023 9 | * 10 | */ 11 | #include 12 | using namespace std; 13 | class Solution 14 | { 15 | public: 16 | vector nums; 17 | int fib(int n) 18 | { 19 | if (n <= 0) 20 | return 0; 21 | 22 | nums.resize(n + 1, 1); 23 | for (int i = 2; i < n + 1; i++) 24 | { 25 | nums[i] = nums[i - 1] + nums[i - 2]; 26 | } 27 | 28 | return nums[n]; 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/integer-break.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file integer-break.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-15 7 | * 343. 整数拆分 8 | * https://leetcode.cn/problems/integer-break/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | using namespace std; 14 | 15 | class Solution { 16 | public: 17 | int integerBreak(int n) { 18 | if (n <= 3) return 1 * (n - 1); 19 | // 初始化 20 | vector dp(n + 1, 0); 21 | dp[1] = 1; 22 | dp[2] = 2; 23 | dp[3] = 3; 24 | for (int i = 4; i <= n ; i++) { 25 | //尝试不同的拆分,保留最大的情况 26 | for (int j = 1; j <= i / 2; j++) { 27 | dp[i] = max(dp[i], dp[i - j] * dp[j]); 28 | } 29 | } 30 | return dp[n]; 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/min-cost-climbing-stairs.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file min-cost-climbing-stairs.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-15 7 | * 746. 使用最小花费爬楼梯 8 | * https://leetcode.cn/problems/min-cost-climbing-stairs/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | class Solution 16 | { 17 | public: 18 | vector mincost; 19 | int minCostClimbingStairs(vector &cost) 20 | { 21 | int n = cost.size(); 22 | mincost.resize(n + 1, 0); 23 | 24 | for (int i = 2; i < n + 1; i++) 25 | { 26 | int c1 = mincost[i - 2] + cost[i - 2]; 27 | int c2 = mincost[i - 1] + cost[i - 1]; 28 | 29 | mincost[i] = c1 < c2 ? c1 : c2; 30 | } 31 | return mincost[n]; 32 | } 33 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/ones-and-zeroes.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ones-and-zeroes.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-16 7 | * 474. 一和零 8 | * https://leetcode.cn/problems/ones-and-zeroes/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | 16 | using namespace std; 17 | 18 | class Solution 19 | { 20 | public: 21 | int findMaxForm(vector &strs, int m, int n) 22 | { 23 | vector> DP(m + 1, vector(n + 1, 0)); 24 | 25 | for (string str : strs) 26 | { 27 | int num0 = 0; 28 | int num1 = 0; 29 | for (char c : str) 30 | { 31 | if (c == '0') 32 | num0++; 33 | else 34 | num1++; 35 | } 36 | for (int i = m; i >= num0; i--) 37 | { 38 | for (int j = n; j >= num1; j--) 39 | { 40 | DP[i][j] = max(DP[i - num0][j - num1] + 1, DP[i][j]); 41 | } 42 | } 43 | } 44 | 45 | return DP[m][n]; 46 | } 47 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/unique-binary-search-trees.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file unique-binary-search-trees.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-16 7 | * 96. 不同的二叉搜索树 8 | * https://leetcode.cn/problems/unique-binary-search-trees/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | vector dp; 20 | int numTrees(int n) 21 | { 22 | dp.resize(n + 1, 0); 23 | dp[0] = 1; 24 | for (int i = 1; i < n + 1; i++) 25 | { 26 | // 头结点占一个 27 | // 剩余的i-1个分配 28 | for (int j = 0; j < (i - 1) >> 1; j++) 29 | dp[i] += 2 * dp[j] * dp[i - 1 - j]; 30 | 31 | if ((i - 1) % 2 == 0) 32 | dp[i] += dp[(i - 1) >> 1] * dp[(i - 1) >> 1]; // 偶数个,左右两侧分配个数相等属于一种情况 33 | else 34 | dp[i] += 2 * dp[(i - 1) >> 1] * dp[((i - 1) >> 1) + 1]; // 奇数个,左右两侧分配个数不相等 35 | } 36 | return dp[n]; 37 | } 38 | }; 39 | 40 | int main(int argc, char **argv) 41 | { 42 | cout << endl 43 | << "输入 n" << endl; 44 | Solution solution; 45 | int n; 46 | cin >> n; 47 | cout << endl; 48 | solution.numTrees(n); 49 | // solution.printDP(); 50 | cout << endl; 51 | return 0; 52 | } -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/unique-paths-ii.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file unique-paths-ii.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-15 7 | * 63. 不同路径 II 8 | * https://leetcode.cn/problems/unique-paths-ii/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | using namespace std; 14 | 15 | class Solution 16 | { 17 | public: 18 | int uniquePathsWithObstacles(vector> &obstacleGrid) 19 | { 20 | int m = obstacleGrid.size(); 21 | int n = obstacleGrid[0].size(); 22 | 23 | vector> DP(m, vector(n, 0)); 24 | 25 | // 初始化 26 | for (int i = 0; i < m; i++) 27 | { 28 | if (obstacleGrid[i][0] == 1) 29 | break; 30 | DP[i][0] = 1; 31 | } 32 | 33 | for (int i = 0; i < n; i++) 34 | { 35 | if (obstacleGrid[0][i] == 1) 36 | break; 37 | DP[0][i] = 1; 38 | } 39 | 40 | //填充DP 41 | for (int i = 1; i < m; i++) 42 | { 43 | for (int j = 1; j < n; j++) 44 | { 45 | if (obstacleGrid[i][j] == 1) 46 | DP[i][j] == 0; 47 | else 48 | DP[i][j] = DP[i - 1][j] + DP[i][j - 1]; 49 | } 50 | } 51 | return DP[m - 1][n - 1]; 52 | } 53 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/unique-paths.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file unique-paths.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-15 7 | * 62. 不同路径 8 | * https://leetcode.cn/problems/unique-paths/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | int uniquePaths(int m, int n) 20 | { 21 | vector> DP(m, vector(n, 1)); 22 | 23 | for (int i = 1; i < m; i++) 24 | { 25 | for (int j = 1; j < n; j++) 26 | { 27 | DP[i][j] = DP[i - 1][j] + DP[i][j - 1]; 28 | } 29 | } 30 | return DP[m - 1][n - 1]; 31 | } 32 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/完全背包问题/coin-change.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file coin-change.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-16 7 | * 322. 零钱兑换 8 | * https://leetcode.cn/problems/coin-change/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | using namespace std; 16 | class Solution 17 | { 18 | public: 19 | int coinChange(vector &coins, int amount) 20 | { 21 | vector dp(amount + 1, -1); 22 | 23 | for (int i = 0; i < coins.size(); i++) 24 | { 25 | for (int j = coins[i]; j <= amount; j++) 26 | { 27 | if (j == coins[i]) 28 | { 29 | dp[j] = 1; 30 | } 31 | else if (dp[j] != -1 && dp[j - coins[i]] != -1) 32 | dp[j] = min(dp[j], dp[j - coins[i]] + 1); 33 | else if (dp[j] == -1 && dp[j - coins[i]] != -1) 34 | { 35 | dp[j] = dp[j - coins[i]] + 1; 36 | } 37 | else if (dp[j] != -1 && dp[j - coins[i]] == -1) 38 | { 39 | dp[j] = dp[j]; 40 | } 41 | else 42 | { 43 | dp[j] = -1; 44 | } 45 | } 46 | } 47 | 48 | return dp[amount]; 49 | } 50 | }; 51 | 52 | class Solution 53 | { 54 | public: 55 | int coinChange(vector &coins, int amount) 56 | { 57 | vector dp(amount + 1, INT_MAX); 58 | dp[0] = 0; 59 | for (int i = 0; i < coins.size(); i++) 60 | { 61 | for (int j = coins[i]; j <= amount; j++) 62 | { 63 | if (dp[j - coins[i]] != INT_MAX) 64 | dp[j] = min(dp[j], dp[j - coins[i]] + 1); 65 | } 66 | } 67 | if (dp[amount] == INT_MAX) 68 | return -1; 69 | return dp[amount]; 70 | } 71 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/完全背包问题/combination-sum-iv.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file combination-sum-iv.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-16 7 | * 377. 组合总和 Ⅳ 8 | * https://leetcode.cn/problems/combination-sum-iv/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | class Solution 18 | { 19 | public: 20 | int combinationSum4(vector &nums, int target) 21 | { 22 | 23 | vector DP(target + 1, 0); 24 | DP[0] = 1; 25 | 26 | for (int i = 0; i <= target; i++) 27 | { 28 | for (int j = 0; j < nums.size(); j++) 29 | { 30 | if (i - nums[j] >= 0) 31 | { 32 | DP[i] += DP[i - nums[j]]; 33 | } 34 | } 35 | } 36 | 37 | return DP[target]; 38 | } 39 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/完全背包问题/perfect-squares.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file perfect-squares.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-16 7 | * 279. 完全平方数 8 | * https://leetcode.cn/problems/perfect-squares/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | #include 14 | using namespace std; 15 | class Solution 16 | { 17 | public: 18 | int numSquares(int n) 19 | { 20 | vector dp(n + 1, INT_MAX); 21 | dp[0] = 0; 22 | for (int i = 1; i * i <= n; i++) 23 | { 24 | for (int j = i * i; j <= n; j++) 25 | { 26 | if (dp[j - i * i] != INT_MAX) 27 | dp[j] = min(dp[j], 1 + dp[j - i * i]); 28 | } 29 | } 30 | if (dp[n] == INT_MAX) 31 | return -1; 32 | return dp[n]; 33 | } 34 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/打家劫舍问题/house-robber-ii.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file house-robber-ii.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-18 7 | * 213. 打家劫舍 II 8 | * https://leetcode.cn/problems/house-robber-ii/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | int rob(vector &nums) 20 | { 21 | if (nums.size() == 0) 22 | return 0; 23 | if (nums.size() == 1) 24 | return nums[0]; 25 | if (nums.size() == 2) 26 | return max(nums[0], nums[1]); 27 | 28 | vector dp1(nums.size() - 1, 0); 29 | dp1[0] = nums[0]; 30 | dp1[1] = max(nums[0], nums[1]); 31 | for (int i = 2; i < nums.size() - 1; i++) 32 | { 33 | dp1[i] = max(nums[i] + dp1[i - 2], dp1[i - 1]); 34 | } 35 | vector dp2(nums.size() - 1, 0); 36 | dp2[0] = nums[1]; 37 | dp2[1] = max(nums[1], nums[2]); 38 | for (int i = 3; i < nums.size(); i++) 39 | { 40 | dp2[i - 1] = max(nums[i] + dp2[i - 3], dp2[i - 2]); 41 | } 42 | return max(dp1[nums.size() - 2], dp2[nums.size() - 2]); 43 | } 44 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/打家劫舍问题/house-robber-iii.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file house-robber-iii.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-18 7 | * 337. 打家劫舍 III 8 | * https://leetcode.cn/problems/house-robber-iii/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | 14 | using namespace std; 15 | struct TreeNode 16 | { 17 | int val; 18 | TreeNode *left; 19 | TreeNode *right; 20 | TreeNode() : val(0), left(nullptr), right(nullptr) {} 21 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 22 | TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 23 | }; 24 | 25 | class Solution 26 | { 27 | public: 28 | vector dp(TreeNode *root) 29 | { 30 | if (root == nullptr) 31 | return {0, 0}; 32 | 33 | vector dpleft = dp(root->left); 34 | vector dpright = dp(root->right); 35 | int dp0 = max(dpleft[0], dpleft[1]) + max(dpright[0], dpright[1]); 36 | int dp1 = root->val + dpleft[0] + dpright[0]; 37 | return {dp0, dp1}; 38 | } 39 | int rob(TreeNode *root) 40 | { 41 | vector dproot = dp(root); 42 | return max(dproot[0], dproot[1]); 43 | } 44 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/打家劫舍问题/house-robber.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file house-robber.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-18 7 | * 198. 打家劫舍 8 | * https://leetcode.cn/problems/house-robber/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | 15 | using namespace std; 16 | class Solution 17 | { 18 | public: 19 | int rob(vector &nums) 20 | { 21 | if (nums.size() == 0) 22 | return 0; 23 | if (nums.size() == 1) 24 | return nums[0]; 25 | 26 | vector dp; 27 | dp.resize(nums.size(), 0); 28 | dp[0] = nums[0]; 29 | dp[1] = max(nums[0], nums[1]); 30 | 31 | for (int i = 2; i < nums.size(); i++) 32 | { 33 | dp[i] = max(dp[i - 1], dp[i - 2] + nums[i]); 34 | } 35 | return dp[nums.size() - 1]; 36 | } 37 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/购买股票问题/best-time-to-buy-and-sell-stock-ii.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file best-time-to-buy-and-sell-stock-ii.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-12 7 | * 122. 买卖股票的最佳时机 II 8 | * https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | // 贪心 16 | class Solution 17 | { 18 | public: 19 | int maxProfit(vector &prices) 20 | { 21 | if (prices.size() <= 1) 22 | return 0; 23 | 24 | int result = 0; 25 | int pre = prices[0]; 26 | 27 | for (int i = 1; i < prices.size(); i++) 28 | { 29 | result += prices[i] - pre > 0 ? prices[i] - pre : 0; 30 | pre = prices[i]; 31 | } 32 | return result; 33 | } 34 | }; 35 | 36 | class Solution 37 | { 38 | public: 39 | int maxProfit(vector &prices) 40 | { 41 | int len = prices.size(); 42 | if (len == 0) 43 | return 0; 44 | // dp[i][1] 第i天持有股票 45 | // dp[i][0] 第i天不持有股票 46 | vector> dp(len, vector(2)); 47 | dp[0][1] = -prices[0]; 48 | dp[0][0] = 0; 49 | for (int i = 1; i < len; i++) 50 | { 51 | dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]); 52 | dp[i][0] = max(dp[i - 1][1] + prices[i], dp[i - 1][0]); 53 | } 54 | return dp[len - 1][0]; 55 | } 56 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/购买股票问题/best-time-to-buy-and-sell-stock-iii.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file best-time-to-buy-and-sell-stock-iii.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-18 7 | * 123. 买卖股票的最佳时机 III 8 | * https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | using namespace std; 14 | 15 | class Solution 16 | { 17 | public: 18 | int maxProfit(vector &prices) 19 | { 20 | if (prices.size() <= 1) 21 | return 0; 22 | int size = prices.size(); 23 | vector> dp(size, vector(5, 0)); 24 | 25 | dp[0][0] = 0; // 从来没买卖过 26 | dp[0][1] = -prices[0]; // 第一次买后 27 | dp[0][2] = 0; // 第一次卖后 28 | dp[0][3] = -prices[0]; // 第二次买后 29 | dp[0][4] = 0; // 第二次卖后 30 | 31 | for (int i = 1; i < size; i++) 32 | { 33 | int dp0 = 0; 34 | int dp1 = max(dp[i - 1][1], dp[i - 1][0] - prices[i]); 35 | int dp2 = max(dp[i - 1][2], dp[i - 1][1] + prices[i]); 36 | int dp3 = max(dp[i - 1][3], dp[i - 1][2] - prices[i]); 37 | int dp4 = max(dp[i - 1][4], dp[i - 1][3] + prices[i]); 38 | 39 | dp[i] = {dp0,dp1,dp2,dp3,dp4}; 40 | } 41 | return dp[size-1][4]; 42 | } 43 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/购买股票问题/best-time-to-buy-and-sell-stock-iv.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file best-time-to-buy-and-sell-stock-iv.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-18 7 | * 188. 买卖股票的最佳时机 IV 8 | * @copyright Copyright (c) 2023 9 | * 10 | */ 11 | #include 12 | using namespace std; 13 | 14 | class Solution 15 | { 16 | public: 17 | int maxProfit(int k, vector &prices) 18 | { 19 | int size = prices.size(); 20 | if (size == 0) 21 | return 0; 22 | vector> dp(size, vector(2 * k + 1, 0)); 23 | // j = 0 , 无操作 24 | // j = 2*k-1 ,第k次买入 25 | // j = 2*k ,第k次卖出 26 | 27 | for (int i = 0; i < 2 * k + 1; i++) 28 | { 29 | if (i % 2 == 1) 30 | dp[0][i] = -prices[0]; 31 | } 32 | 33 | for (int i = 1; i < size; i++) 34 | { 35 | for (int j = 0; j < 2 * k - 1; j += 2) 36 | { 37 | dp[i][j + 1] = max(dp[i - 1][j] - prices[i], dp[i - 1][j + 1]); 38 | dp[i][j + 2] = max(dp[i - 1][j + 1] + prices[i], dp[i - 1][j + 2]); 39 | } 40 | } 41 | 42 | return dp[size - 1][2 * k]; 43 | } 44 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/购买股票问题/best-time-to-buy-and-sell-stock-with-cooldown.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file best-time-to-buy-and-sell-stock-with-cooldown.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-18 7 | * 309. 最佳买卖股票时机含冷冻期 8 | * https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-cooldown/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | int maxProfit(vector &prices) 20 | { 21 | int size = prices.size(); 22 | if (size == 0) 23 | return 0; 24 | vector> dp(size, vector(4, 0)); 25 | 26 | dp[0][0] = -prices[0]; // 当天买入或者买入过了 27 | dp[0][1] = 0; // 卖过了,能买入新的 28 | dp[0][2] = 0; // 当天卖了 29 | dp[0][3] = 0; // 冷冻期 30 | 31 | for (int i = 1; i < size; i++) 32 | { 33 | dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][3] - prices[i], dp[i - 1][1] - prices[i])); 34 | dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]); 35 | dp[i][2] = dp[i - 1][0] + prices[i]; 36 | dp[i][3] = dp[i - 1][2]; 37 | } 38 | 39 | return max(dp[size - 1][3], max(dp[size - 1][2], dp[size - 1][1])); 40 | } 41 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/购买股票问题/best-time-to-buy-and-sell-stock-with-transaction-fee.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file best-time-to-buy-and-sell-stock-with-transaction-fee.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-18 7 | * 714. 买卖股票的最佳时机含手续费 8 | * https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | int maxProfit(vector &prices, int fee) 20 | { 21 | 22 | int size = prices.size(); 23 | if (size == 0) 24 | return 0; 25 | 26 | vector> dp(size, vector(2, 0)); 27 | 28 | dp[0][1] = -prices[0]; 29 | 30 | for (int i = 1; i < size; i++) 31 | { 32 | dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i] - fee); 33 | dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]); 34 | } 35 | return dp[size - 1][0]; 36 | } 37 | }; -------------------------------------------------------------------------------- /算法学习/11.DynamicProgramming/购买股票问题/best-time-to-buy-and-sell-stock.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file best-time-to-buy-and-sell-stock.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-18 7 | * 121. 买卖股票的最佳时机 8 | * https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | 15 | using namespace std; 16 | 17 | class Solution 18 | { 19 | public: 20 | int maxProfit(vector &prices) 21 | { 22 | int len = prices.size(); 23 | if (len == 0) 24 | return 0; 25 | // dp[i][1] 第i天持有股票 26 | // dp[i][0] 第i天不持有股票 27 | vector> dp(len, vector(2)); 28 | dp[0][1] = -prices[0]; 29 | dp[0][0] = 0; 30 | for (int i = 1; i < len; i++) 31 | { 32 | dp[i][1] = max(dp[i - 1][1], -prices[i]);//没买早,之前买早了,做时光机退了,今天买 33 | dp[i][0] = max(dp[i - 1][1] + prices[i], dp[i - 1][0]);//今天卖,之前卖过了或之前没买 34 | } 35 | return dp[len - 1][0]; 36 | } 37 | }; -------------------------------------------------------------------------------- /算法学习/12.GraphAlgorithm/Dijkstra/bfs.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | // 邻接矩阵 5 | class Node 6 | { 7 | public: 8 | int value; 9 | Node *parent; 10 | 11 | Node(int _value) 12 | { 13 | value = _value; 14 | } 15 | }; 16 | 17 | void bfsdijkstra(vector &nodes, vector> distance, int start, int target) 18 | { 19 | // bfs搜索穿起来 20 | int n = nodes.size(); 21 | queue indexs; 22 | indexs.push(start); 23 | nodes[start]->parent = nullptr; 24 | 25 | while (!indexs.empty()) 26 | { 27 | int size = indexs.size(); 28 | for (int i = 0; i < size; i++) 29 | { 30 | int current_index = indexs.front(); 31 | indexs.pop(); 32 | for (int j = 0; j < n; j++) 33 | { 34 | if (distance[current_index][j] != -1 && nodes[j]->parent == nodes[j]) 35 | { 36 | indexs.push(j); 37 | nodes[j] = nodes[current_index]; 38 | if (j == target) 39 | return; 40 | } 41 | } 42 | } 43 | } 44 | } 45 | 46 | int main() 47 | { 48 | 49 | int n = 5; 50 | int value[n]; // nodes 51 | // int distance[n][n]; // 节点node之间的距离 52 | // memset(distance, -1, n * n * sizeof(int)); // 先填充-1表示没有连接边 53 | 54 | vector> distance(n, vector(n, -1)); 55 | vector nodes; // 记录反向路径 56 | // 初始化 57 | for (int i = 0; i < n; i++) 58 | { 59 | Node *node = new Node(value[i]); 60 | node->parent = node; 61 | } 62 | 63 | int end; 64 | vector result; 65 | Node *current_node = nodes[end]; 66 | while (current_node != nullptr) 67 | { 68 | result.push_back(current_node->value); 69 | current_node = current_node->parent; 70 | } 71 | reverse(result.begin(), result.end()); 72 | 73 | return 0; 74 | } -------------------------------------------------------------------------------- /算法学习/12.GraphAlgorithm/Dijkstra/dijkstra.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | class Edge; 5 | class Node; 6 | 7 | class Node 8 | { 9 | public: 10 | int value; 11 | int in; 12 | int out; 13 | vector next; 14 | vector edges; 15 | 16 | Node(int _value) 17 | { 18 | value = _value; 19 | in = 0; 20 | out = 0; 21 | } 22 | }; 23 | 24 | class Edge 25 | { 26 | public: 27 | int weight; 28 | Node *from; 29 | Node *to; 30 | 31 | Edge(int _weight, Node *_from, Node *_to) 32 | { 33 | weight = _weight; 34 | from = _from; 35 | to = _to; 36 | } 37 | }; 38 | 39 | class Graph 40 | { 41 | public: 42 | unordered_map nodes; 43 | unordered_set edges; 44 | Graph() 45 | { 46 | } 47 | }; 48 | 49 | class Dijkstra 50 | { 51 | }; 52 | 53 | int main() 54 | { 55 | return 0; 56 | } -------------------------------------------------------------------------------- /算法学习/12.GraphAlgorithm/UnionSet/并查集.md: -------------------------------------------------------------------------------- 1 | # 并查集 2 | ## 时间复杂度 3 | 调用频繁单次时间复杂度$O(1)$ -------------------------------------------------------------------------------- /算法学习/12.GraphAlgorithm/bfs/keys-and-rooms.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file keys-and-rooms.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-07-19 7 | * 841. 钥匙和房间 8 | * https://leetcode.cn/problems/keys-and-rooms/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | class Solution 18 | { 19 | public: 20 | bool canVisitAllRooms(vector> &rooms) 21 | { 22 | 23 | vector visited(rooms.size(), false); 24 | queue keys; 25 | visited[0] = true; 26 | int count = 1; 27 | for (int i = 0; i < rooms[0].size(); i++) 28 | { 29 | keys.push(rooms[0][i]); 30 | } 31 | 32 | while (!keys.empty()) 33 | { 34 | int current_room = keys.front(); 35 | keys.pop(); 36 | if (visited[current_room]) 37 | { 38 | continue; 39 | } 40 | else 41 | { 42 | visited[current_room] = true; 43 | count++; 44 | } 45 | for (int i = 0; i < rooms[current_room].size(); i++) 46 | { 47 | keys.push(rooms[current_room][i]); 48 | } 49 | } 50 | 51 | return count == rooms.size(); 52 | } 53 | }; -------------------------------------------------------------------------------- /算法学习/12.GraphAlgorithm/dfs/number-of-islands.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file number-of-islands.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-07-18 7 | * 200.岛屿数量 8 | * https://leetcode.cn/problems/number-of-islands/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | // 深度优先搜索 17 | class Solution 18 | { 19 | public: 20 | int direction[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; 21 | void dfs(vector> &grid, vector> &visited, int x, int y) 22 | { 23 | for (int i = 0; i < 4; i++) 24 | { 25 | int next_x = x + direction[i][0]; 26 | int next_y = y + direction[i][1]; 27 | 28 | if (next_x < 0 || next_x >= grid.size() || next_y < 0 || next_y >= grid[0].size()) 29 | { 30 | continue; 31 | } 32 | 33 | if (!visited[next_x][next_y] && grid[next_x][next_y] == '1') 34 | { 35 | visited[next_x][next_y] = true; 36 | dfs(grid, visited, next_x, next_y); 37 | } 38 | } 39 | } 40 | 41 | int numIslands(vector> &grid) 42 | { 43 | int n = grid.size(); 44 | int m = grid[0].size(); 45 | vector> visited(n, vector(m, false)); 46 | 47 | int result = 0; 48 | for (int i = 0; i < n; i++) 49 | { 50 | for (int j = 0; j < m; j++) 51 | { 52 | if (!visited[i][j] && grid[i][j] == '1') 53 | { 54 | visited[i][j] = true; 55 | result++; 56 | dfs(grid,visited,i,j); 57 | } 58 | } 59 | } 60 | return result; 61 | } 62 | }; -------------------------------------------------------------------------------- /算法学习/12.GraphAlgorithm/图算法.md: -------------------------------------------------------------------------------- 1 | # 图算法 2 | ## 图 3 | 顶点node 4 | 边edge 5 | 存储方式:邻接表、邻接矩阵等 6 | 7 | 邻接矩阵 8 | ```c++ 9 | int n = 5; 10 | int value[n];//nodes 11 | int distance[n][n];//节点node之间的距离 12 | ``` 13 | 14 | 有向图结构,适用性比较强 15 | ```c++ 16 | class Graph 17 | { 18 | public: 19 | Graph() 20 | { 21 | } 22 | unordered_map vertexs; 23 | unordered_set<*edge> edges; 24 | }; 25 | 26 | template 27 | class Node 28 | { 29 | public: 30 | node( T value_):in(0),out(0); 31 | { 32 | value = value_; 33 | } 34 | T value; 35 | int in;//入度 36 | int out;//出度 37 | vector edges; 38 | vector nexts; 39 | }; 40 | 41 | class Edge 42 | { 43 | public: 44 | edge(int distance_,Node * from_,Node * to_) 45 | { 46 | distance = distance_; 47 | from = from_; 48 | to = to_; 49 | } 50 | 51 | Node *from; 52 | Node *to; 53 | int distance; 54 | }; 55 | ``` 56 | 57 | - 链式前向星 58 | 节省动态内存空间 59 | ```c++ 60 | int head[];//头边号 61 | int next[];//下一个边的编号 62 | int to[];//去往的点 63 | 64 | // 65 | 1 66 | 0 67 | 3 68 | 69 | ``` 70 | 71 | ## 遍历 72 | 73 | ### 广度遍历优先 bfs 74 | 75 | ### 深度优先算法 dfs 76 | 77 | ## 拓扑排序 78 | 问题:给定依赖关系(有向无环图),安排编译顺序 79 | 80 | 依次找入度为0的顶点V删除,并删除顶点V的连接边的影响 81 | ### 题目: 82 | - [207.课程表](https://leetcode.cn/problems/course-schedule/) 83 | 84 | ## 最小生成树 85 | 删除边,使得所有边的权重和最小,使得联通区域不变,求剩余的边 86 | ### K算法 87 | 1. 对边的权重排序 88 | 2. 遍历所有的边,权重最小的边连接的两个顶点未联通,则保留该边,否则剔除该边(建议使用并查集合并) 89 | ### P算法 90 | 1. 随机选择一个顶点V,记录所有连接的边`priority_queue edges小根堆, unordered_set`,记录已经连接的顶点`unordered_set visited` 91 | 2. 从所有记录的连接边中选择权重最小的边`edges.top()`,如果该连接边E连接到新的顶点`V`,则保留该边E,并记录新顶点的连接边`visited.insert(V)`。重复step2,直至连接完所有顶点。 92 | 93 | ## Dijkstra 94 | 计算两个节点之间的最大距离,edge不能有负数 95 | 广度优先更新当前最短距离节点到节连的所有节的最短距离,完成后固定当前节点 96 | 97 | 98 | # 参考 99 | - [图算法](https://github.com/algorithmzuo/algorithmbasic2020/tree/master/src/class16) -------------------------------------------------------------------------------- /算法学习/2. Array/minimum-size-subarray-sum.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file minimum-size-subarray-sum.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-05 7 | * 209. 长度最小的子数组 8 | * https://leetcode.cn/problems/minimum-size-subarray-sum/ 9 | * - 长度最小的子数组 10 | * - j是划窗右端点,for循环变量j 11 | * - i是划窗左端点,每次判断是否更新minnums 12 | * @copyright Copyright (c) 2022 13 | * 14 | */ 15 | 16 | #include 17 | #include 18 | using namespace std; 19 | 20 | class Solution 21 | { 22 | public: 23 | int minSubArrayLen(int target, vector &nums) 24 | { 25 | int minnums = nums.size() + 1; 26 | int i = 0, j = 0, sum = 0; 27 | for (; j < nums.size(); j++) 28 | { 29 | sum += nums[j]; 30 | while (sum >= target) 31 | { 32 | if (j - i + 1 < minnums) 33 | minnums = j - i + 1; 34 | sum -= nums[i]; 35 | i++; 36 | } 37 | } 38 | return minnums == nums.size() + 1 ? 0 : minnums; 39 | } 40 | }; 41 | 42 | int main(int argc, char **argv) 43 | { 44 | int n; 45 | cout << "输入数组数组大小 n" << std::endl; 46 | cin >> n; 47 | 48 | vector nums(n); 49 | int target; 50 | cout << "输入n个正整数作为输入" << std::endl; 51 | for (int i = 0; i < n; i++) 52 | { 53 | cin >> nums[i]; 54 | } 55 | cout << "输入数组和" << endl; 56 | cin >> target; 57 | 58 | Solution solution; 59 | int minnums = solution.minSubArrayLen(target, nums); 60 | 61 | cout << "输入数组:" << endl; 62 | for (int i = 0; i < n; i++) 63 | { 64 | cout << nums[i] << " "; 65 | } 66 | 67 | cout << endl 68 | << "最短数组长度:" << endl 69 | << minnums << endl; 70 | 71 | cout <<"-----结束-----"<< endl; 72 | return 0; 73 | } -------------------------------------------------------------------------------- /算法学习/2. Array/remove-duplicates-from-sorted-array.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file remove-duplicates-from-sorted-array.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-04-13 7 | * 26. 删除有序数组中的重复项 8 | * https://leetcode.cn/problems/remove-duplicates-from-sorted-array/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | class Solution 18 | { 19 | public: 20 | int removeDuplicates(vector &nums) 21 | { 22 | //起始位置从 0 ,1开始 23 | //双指针方法 24 | int p1 = 0; 25 | int p2 = 1; 26 | while (p2 < nums.size()) 27 | { 28 | if (nums[p2] == nums[p1]) 29 | { 30 | p2++; 31 | } 32 | else 33 | { 34 | nums[++p1] = nums[p2++]; 35 | } 36 | } 37 | return p1 + 1; 38 | } 39 | }; -------------------------------------------------------------------------------- /算法学习/2. Array/remove-element.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file remove-element.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-05 7 | * 27. 移除元素 8 | * https://leetcode.cn/problems/remove-element/ 9 | * - 双指针移除制定元素 10 | * @copyright Copyright (c) 2022 11 | * 12 | */ 13 | 14 | #include 15 | #include 16 | using namespace std; 17 | 18 | class Solution 19 | { 20 | public: 21 | int removeElement(vector &nums, int val) 22 | { 23 | int j = 0; 24 | for (int i = 0; i < nums.size(); i++) 25 | { 26 | if (nums[i] != val) 27 | { 28 | nums[j] = nums[i]; 29 | j++; 30 | } 31 | } 32 | nums.resize(j); 33 | return j; 34 | } 35 | }; 36 | 37 | int main(int argc, char **argv) 38 | { 39 | int n; 40 | cout << "输入数组数组大小 n" << std::endl; 41 | cin >> n; 42 | 43 | vector nums(n); 44 | int val; 45 | cout << "输入n个数字作为原数组" << std::endl; 46 | for (int i = 0; i < n; i++) 47 | { 48 | cin >> nums[i]; 49 | } 50 | cout << "输入删除数字" << std::endl; 51 | cin >> val; 52 | 53 | Solution solution; 54 | 55 | std::cout << "原来数组:" << std::endl; 56 | for (int i = 0; i < nums.size(); i++) 57 | { 58 | cout << nums[i] << " "; 59 | } 60 | std::cout << endl 61 | << "新的数组大小:" << solution.removeElement(nums, val) << std::endl; 62 | std::cout << "新的数组:" << std::endl; 63 | for (int i = 0; i < nums.size(); i++) 64 | { 65 | cout << nums[i] << " "; 66 | } 67 | std::cout << std::endl; 68 | 69 | cout << "-----结束-----" << endl; 70 | return 0; 71 | } -------------------------------------------------------------------------------- /算法学习/2. Array/数组.md: -------------------------------------------------------------------------------- 1 | # 数组理论基础 2 | 内存空间地址连续 3 | 删除or增加元素时候需要移动其他元素地址 4 | 数组的元素是不能删的,只能覆盖。 5 | 6 | # 常用方法 7 | ## 二分法 8 | 方法对比|时间复杂度|空间复杂度 9 | -|-|- 10 | 暴力解法|O(n)| 11 | 二分法|O(logn)| 12 | 13 | 应用: 14 | 1. 有序数组查找目标值 15 | 16 | ## 双指针法 17 | 方法对比|时间复杂度|空间复杂度 18 | -|-|- 19 | 暴力解法|O(n^2)| 20 | 双指针法|O(n)| 21 | 22 | 应用: 23 | 1.移除元素 24 | 25 | ## 滑动窗口 26 | 方法对比|时间复杂度|空间复杂度 27 | -|-|- 28 | 暴力解法|O(n^2)| 29 | 双指针法|O(n)| 30 | 31 | 应用: 32 | 1.求解数组中满足条件的一段连续数组 33 | 34 | ## 模拟行为 35 | 主要就是处理循环,思路如下 36 | 1. 边界条件选择,应该保持一致性。 37 | - `[left,right]`或者`[left,right)` 38 | 2. 计算循环次数or循环终止条件。 39 | - 终止条件,`a < b`或者`a <= b` 40 | - 每次循环起点,一般是关于循环次数n的函数`f(n)` 41 | 3. 计算循环次数时候,考虑特殊情形。 42 | - 一般是奇数偶数情况处理方式不同,`if(n%2==1){}` 43 | 44 | 45 | # 瞎BB 46 | 1. 主要关注各种边界条件开闭区间 -------------------------------------------------------------------------------- /算法学习/3. NodeList/相交、环/linked-list-cycle.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file linked-list-cycle.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-04-20 7 | * https://leetcode.cn/problems/linked-list-cycle/submissions/ 8 | * @copyright Copyright (c) 2023 9 | * 10 | */ 11 | 12 | #include 13 | using namespace std; 14 | 15 | /** 16 | * @brief 链表节点的定义 17 | * 18 | */ 19 | struct ListNode 20 | { 21 | int val; 22 | ListNode *next; 23 | ListNode() : val(0), next(nullptr) {} 24 | ListNode(int x) : val(x), next(nullptr) {} 25 | ListNode(int x, ListNode *next) : val(x), next(next) {} 26 | }; 27 | 28 | 29 | class Solution { 30 | public: 31 | bool hasCycle(ListNode *head) { 32 | if(head==nullptr||head->next==nullptr) 33 | return false; 34 | ListNode *p1 = head->next; 35 | ListNode *p2 = head->next->next; 36 | while(p2!=nullptr&&p2->next!=nullptr) 37 | { 38 | if(p1==p2) 39 | return true; 40 | p1 = p1->next; 41 | p2 = p2->next->next; 42 | 43 | } 44 | return false; 45 | 46 | } 47 | }; 48 | 49 | -------------------------------------------------------------------------------- /算法学习/3. NodeList/翻转链表/reverse-nodes-in-k-group: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2hanhan/algorithm/abe1ace7446ea6184b68e0e401ba940af7e194c8/算法学习/3. NodeList/翻转链表/reverse-nodes-in-k-group -------------------------------------------------------------------------------- /算法学习/3. NodeList/链表.md: -------------------------------------------------------------------------------- 1 | # 链表理论基础 2 | 链表通过指针串联在一起的线性结构。 3 | 链表的入口,链表的头结点,head 4 | 链表中的节点在内存中**不是连续分布**的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。 5 | 6 | ## 节点组成 7 | 1. 数据区域 8 | 2. 指针区域 9 | - 存放指向下一节点的指针 10 | - 最后一个节点的指针指向NULL 11 | 12 | ## 链表类型 13 | ### 单链表 14 | 15 | ### 双链表 16 | 指针区域有**两个指针**,一个指向下一个节点,一个指向上一个节点。 17 | 18 | ### 循环链表 19 | 链表首尾相连 20 | 21 | ## c++中定义链表 22 | ```c++ 23 | struct ListNode 24 | { 25 | int val; 26 | ListNode *next; 27 | ListNode() : val(0), next(nullptr) {} 28 | ListNode(int x) : val(x), next(nullptr) {} 29 | ListNode(int x, ListNode *next) : val(x), next(next) {} 30 | } 31 | ``` 32 | 33 | ## 基本操作 34 | ### 删除节点 35 | 需要前一个节点的指针才能删除当前节点 36 | ### 添加节点 37 | # 常用方法 38 | 39 | # 注意 40 | 处理完链表返回值通常是指向链表头结点`head`的指针。 41 | 使用虚拟头结点的时候返回值是`dummyhead->next` 42 | 43 | # 瞎BB 44 | 1. 更改链表,时候需要temp暂时存取一下next防止丢失了,还有使用虚拟头节dummyhead点方便对head的操作不需要单独处理。不更改就不用dummyhead 45 | 2. 知道环形链表和链表相交的套路。 -------------------------------------------------------------------------------- /算法学习/3. NodeList/链表合并/merge-two-sorted-lists.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file merge-two-sorted-lists.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-04-19 7 | * 合并两个有序链表 8 | * https://leetcode.cn/problems/merge-two-sorted-lists/ 9 | * 10 | * https://www.bilibili.com/video/BV1UB4y1S7dF?p=39 11 | * @copyright Copyright (c) 2023 12 | * 13 | */ 14 | 15 | 16 | #include 17 | using namespace std; 18 | 19 | /** 20 | * @brief 链表节点的定义 21 | * 22 | */ 23 | struct ListNode 24 | { 25 | int val; 26 | ListNode *next; 27 | ListNode() : val(0), next(nullptr) {} 28 | ListNode(int x) : val(x), next(nullptr) {} 29 | ListNode(int x, ListNode *next) : val(x), next(next) {} 30 | }; 31 | 32 | class Solution { 33 | public: 34 | ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) 35 | { 36 | // 特殊情况有为空的直接返回 37 | if(list1==nullptr||list2==nullptr) 38 | { 39 | return list1==nullptr? list2:list1; 40 | } 41 | 42 | // 进行拼接,pre->next取小的那一个 43 | ListNode *head = list1->val>list2->val?list2:list1; 44 | ListNode *p1 = head->next; 45 | ListNode *p2 = head == list1?list2:list1; 46 | ListNode * pre = head; 47 | 48 | while(p1!=nullptr&&p2!=nullptr) 49 | { 50 | if(p1->valval) 51 | { 52 | pre->next = p1; 53 | p1=p1->next; 54 | } 55 | else 56 | { 57 | pre->next = p2; 58 | p2=p2->next; 59 | } 60 | pre = pre->next; 61 | } 62 | //有一个用完了,则nxet指向没用完的那一个 63 | pre->next = p1==nullptr? p2:p1; 64 | return head; 65 | 66 | } 67 | }; -------------------------------------------------------------------------------- /算法学习/3. NodeList/链表求和/sum-lists-lcci.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file sum-lists-lcci.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-04-19 7 | * 链表求和 8 | * https://leetcode.cn/problems/sum-lists-lcci/ 9 | * 10 | * https://www.bilibili.com/video/BV1UB4y1S7dF?p=38 11 | * @copyright Copyright (c) 2023 12 | * 13 | */ 14 | #include 15 | using namespace std; 16 | 17 | /** 18 | * @brief 链表节点的定义 19 | * 20 | */ 21 | struct ListNode 22 | { 23 | int val; 24 | ListNode *next; 25 | ListNode() : val(0), next(nullptr) {} 26 | ListNode(int x) : val(x), next(nullptr) {} 27 | ListNode(int x, ListNode *next) : val(x), next(next) {} 28 | }; 29 | 30 | 31 | class Solution { 32 | public: 33 | ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) 34 | { 35 | ListNode *dummyhead = new ListNode(0); 36 | ListNode *pre = dummyhead; 37 | int carry = 0; 38 | 39 | while(l1!=nullptr||l2!=nullptr) 40 | { 41 | ListNode *current = new ListNode(0); 42 | int num = carry; 43 | if(l1!=nullptr) 44 | { 45 | num += l1->val; 46 | l1=l1->next; 47 | } 48 | if(l2!=nullptr) 49 | { 50 | num += l2->val; 51 | l2 = l2->next; 52 | } 53 | 54 | int value = num%10; 55 | current->val = value; 56 | carry = num/10; 57 | 58 | pre->next = current; 59 | pre = pre->next; 60 | } 61 | if(carry) 62 | { 63 | ListNode *current = new ListNode(carry); 64 | pre->next =current; 65 | } 66 | return dummyhead->next; 67 | } 68 | }; -------------------------------------------------------------------------------- /算法学习/4.HashMap/4sum-ii.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 4sum-ii.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-22 7 | * 4.6 四数相加II,数组1、2、3、4分别提供a、b、c、d 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | 12 | #include 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | int fourSumCount(vector &nums1, vector &nums2, vector &nums3, vector &nums4) 20 | { 21 | unordered_map sum12; // 22 | 23 | for (int num1 : nums1) 24 | { 25 | for (int num2 : nums2) 26 | { 27 | sum12[num1 + num2]++; 28 | } 29 | } 30 | 31 | int count = 0; 32 | for (int num3 : nums3) 33 | { 34 | for (int num4 : nums4) 35 | { 36 | auto iter = sum12.find(0 - num3 - num4); 37 | if (iter != sum12.end()) // c+d == -(a+b) 38 | count += iter->second; // 统计计算a+b=c+d次数 39 | } 40 | } 41 | 42 | return count; 43 | } 44 | }; -------------------------------------------------------------------------------- /算法学习/4.HashMap/happy-number.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file happy-number.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-22 7 | * 8 | * @copyright Copyright (c) 2022 9 | * - 4.4 快乐数 10 | * - 思路是判断各个位上的数字的平方和可能出现无限循环。 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | int getSum(int sum) 20 | { 21 | int newsum = 0; 22 | while (sum) 23 | { 24 | newsum += (sum % 10) * (sum % 10); // 累计个、十、百、千...的平方 25 | sum /= 10; // 抹除最低位 26 | } 27 | return newsum; 28 | } 29 | bool isHappy(int n) 30 | { 31 | unordered_set sum_set; // 统计出现的sum 32 | 33 | while (true) 34 | { 35 | n = getSum(n); // 计算各位平方和 36 | if (n == 1) 37 | return true; 38 | 39 | if (sum_set.find(n) != sum_set.end()) 40 | { 41 | return false; // sum出现循环则不是快乐数 42 | } 43 | else 44 | { 45 | sum_set.insert(n); // 保存出现过的sum 46 | } 47 | } 48 | } 49 | }; -------------------------------------------------------------------------------- /算法学习/4.HashMap/intersection-of-two-arrays.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file intersection-of-two-arrays.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-22 7 | * 4.3 两个数组的交集 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | 12 | #include 13 | #include 14 | 15 | using namespace std; 16 | 17 | // 使用unorder_set 18 | class Solution 19 | { 20 | public: 21 | vector intersection(vector &nums1, vector &nums2) 22 | { 23 | // 存放交集数字 24 | unordered_set result_set; 25 | 26 | // 有nums1生成set 27 | unordered_set nums_set(nums1.begin(), nums1.end()); 28 | 29 | // 遍历nums2中的数获取交集 30 | for (int num : nums2) 31 | { 32 | if (nums_set.find(num) != nums_set.end()) 33 | result_set.insert(num); 34 | } 35 | 36 | return vector(result_set.begin(), result_set.end()); 37 | } 38 | }; 39 | 40 | // 使用数组求解 41 | class Solution 42 | { 43 | public: 44 | vector intersection(vector &nums1, vector &nums2) 45 | { 46 | // 存放交集数字 47 | unordered_set result_set; 48 | // 统计出现的数字 49 | int record[1001] = {0}; 50 | 51 | for (int num : nums1) 52 | { 53 | record[num] = 1; 54 | } 55 | 56 | for (int num : nums2) 57 | { 58 | if (record[num] == 1) 59 | result_set.insert(num); 60 | } 61 | 62 | return vector(result_set.begin(), result_set.end()); 63 | } 64 | }; -------------------------------------------------------------------------------- /算法学习/4.HashMap/ransom-note.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ransom-note.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-22 7 | * - 4.7 赎金信 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | 12 | #include 13 | #include 14 | 15 | using namespace std; 16 | 17 | // 使用数组 18 | class Solution 19 | { 20 | public: 21 | bool canConstruct(string ransomNote, string magazine) 22 | { 23 | if (ransomNote.size() > magazine.size()) 24 | return false; 25 | 26 | int record[26] = {0}; 27 | for (char s : magazine) 28 | { 29 | record[s - 'a']++; 30 | } 31 | 32 | // 计算是否够用 33 | for (char s : ransomNote) 34 | { 35 | record[s - 'a']--; 36 | if (record[s - 'a'] < 0) 37 | return false; 38 | } 39 | return true; 40 | } 41 | }; 42 | 43 | // unordered_map 44 | // class Solution 45 | // { 46 | // public: 47 | // bool canConstruct(string ransomNote, string magazine) 48 | // { 49 | // if (ransomNote.size() > magazine.size()) 50 | // return false; 51 | 52 | // // 统计可用的 53 | // unordered_map map; 54 | // for (char s : magazine) 55 | // { 56 | // map[s]++; 57 | // } 58 | 59 | // // 计算是否够用 60 | // for (char s : ransomNote) 61 | // { 62 | // map[s]--; 63 | // if (map[s] < 0) 64 | // return false; 65 | // } 66 | // return true; 67 | // } 68 | // }; -------------------------------------------------------------------------------- /算法学习/4.HashMap/two-sum.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file two-sum.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-22 7 | * - 4.5 两数之和 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | 12 | #include 13 | #include 14 | using namespace std; 15 | 16 | // 哈希表方式求解 17 | class Solution 18 | { 19 | public: 20 | vector twoSum(vector &nums, int target) 21 | { 22 | unordered_map map; // 23 | for (int i = 0; i < nums.size(); i++) 24 | { 25 | auto iter = map.find(target - nums[i]); 26 | if (iter != map.end()) 27 | { 28 | return {iter->second, i}; 29 | } 30 | else 31 | { 32 | map.insert(pair(nums[i], i)); 33 | } 34 | } 35 | return {}; 36 | } 37 | }; 38 | 39 | // 遍历方式求解 40 | class Solution 41 | { 42 | public: 43 | vector twoSum(vector &nums, int target) 44 | { 45 | for (int i = 0; i < nums.size(); i++) 46 | { 47 | for (int j = i + 1; j < nums.size(); j++) 48 | { 49 | if (nums[i] + nums[j] == target) 50 | return {i, j}; 51 | } 52 | } 53 | return {}; 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /算法学习/4.HashMap/valid-anagram.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file valid-anagram.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-22 7 | * - 4.2 有效的字母异位词 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | #include 12 | using namespace std; 13 | 14 | class Solution 15 | { 16 | public: 17 | bool isAnagram(string s, string t) 18 | { 19 | // 统计26个字母出现次数 20 | int record[26] = {0}; 21 | 22 | // 统计s中所以字母出现次数 23 | for (int i = 0; i < s.size(); i++) 24 | { 25 | record[s[i] - 'a']++; 26 | } 27 | // s中所以字母出现次数-t中所有字母出现次数 28 | for (int i = 0; i < t.size(); i++) 29 | { 30 | record[t[i] - 'a']--; 31 | } 32 | 33 | // 统计表不全为0返回false 34 | for (int i = 0; i < 26; i++) 35 | { 36 | if (record[i] != 0) 37 | return false; 38 | } 39 | 40 | // 统计表全部为0返回true 41 | return true; 42 | } 43 | }; -------------------------------------------------------------------------------- /算法学习/4.HashMap/哈希表.md: -------------------------------------------------------------------------------- 1 | # 哈希表 2 | 查询时间复杂度 O(1) 3 | 增删时间复杂度 O(1) 4 | 需要使用额外的空间来存储数据,所以占用空间比较大。 5 | > 主要应用: 6 | > 1. 统计出现次数 7 | > 2. 统计交集 8 | ## 哈希表算法基础 9 | 查询时间复杂度O(1) 10 | 11 | ### 哈希表 hash table 12 | ### 哈希函数 hash function 13 | 将键值key映射到哈希表hashtable 14 | 15 | ### 哈希碰撞 16 | 不同键值key映射到hash table的相同索引 17 | 解决方法哈希碰撞有拉链法和线性探测法 18 | #### 拉链法 19 | 发生冲突的元素都被存储在链表中。 20 | #### 线性探测法 21 | 使用线性探测法,一定要保证tableSize大于dataSize。 我们需要依靠哈希表中的空位来解决碰撞问题。 22 | 23 | ## 常见的哈希结构 24 | 1. 数组 25 | 2. set (集合) 26 | 3. map(映射) -------------------------------------------------------------------------------- /算法学习/5.String/repeated-substring-pattern.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file repeated-substring-pattern.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-26 7 | * 5.7 重复的子字符串 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | 12 | #include 13 | #include 14 | using namespace std; 15 | 16 | // 移动匹配 17 | // s+s里面去寻找s 18 | class Solution0 19 | { 20 | public: 21 | bool repeatedSubstringPattern(string s) 22 | { 23 | // s+s掐头去尾 24 | string t = s + s; 25 | t.erase(t.begin()); 26 | t.erase(t.end() - 1); 27 | 28 | // 掐头去尾的s+s中寻找s 29 | if (t.find(s) != std::string::npos) 30 | return true; 31 | return false; 32 | } 33 | }; 34 | 35 | // 使用KMP不减一 36 | class Solution1 37 | { 38 | public: 39 | void getNext(int *next, const string &s) 40 | { 41 | // 不减一的版本 42 | int j = 0; 43 | next[0] = 0; 44 | for (int i = 1; i < s.size(); i++) 45 | { 46 | while (j > 0 && s[i] != s[j]) 47 | { 48 | j = next[j - 1]; 49 | } 50 | if (s[i] == s[j]) 51 | { 52 | j++; 53 | } 54 | next[i] = j; 55 | } 56 | } 57 | bool repeatedSubstringPattern(string s) 58 | { 59 | if (s.size() == 0) 60 | { 61 | return false; 62 | } 63 | 64 | // step 1 计算前缀表next数组 65 | int next[s.size()]; 66 | getNext(next, s); 67 | 68 | // step 2 计算循环数组长度,判断是否循环 69 | // next 数组的最后一位的数值 等于 len-循环数值的长度 70 | // 循环数值的长度 = len - next[len - 1] 71 | int len = s.size(); 72 | // next数组最后一位不为0 并且 数组总长度%循环数组长度==0 73 | if (next[len - 1] != 0 && len % (len - next[len - 1]) == 0) 74 | { 75 | return true; 76 | } 77 | return false; 78 | } 79 | }; 80 | -------------------------------------------------------------------------------- /算法学习/5.String/reverse-string-ii.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file reverse-string-ii.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-26 7 | * 5.2 翻转字符串II 8 | * @copyright Copyright (c) 2022 9 | * - 这个题目的关键是理解化简题目的含义,如果题目直接使用下面的表述接方便理解了 10 | * - 本质上是翻转第 [2*k*i,2*k*i+k)处的字符串 11 | * reverse()函数翻转的是[a,b)迭代器直接的部分 12 | */ 13 | 14 | #include 15 | #include 16 | using namespace std; 17 | 18 | class Solution 19 | { 20 | public: 21 | string reverseStr(string s, int k) 22 | { 23 | for (int i = 0; i < s.size(); i += (2 * k)) 24 | { 25 | if (i + k <= s.size()) 26 | { 27 | reverse(s.begin() + i, s.begin() + i + k + 1); 28 | } 29 | else 30 | { 31 | reverse(s.begin() + 1, s.end()); 32 | } 33 | } 34 | return s; 35 | } 36 | }; 37 | 38 | class Solution 39 | { 40 | public: 41 | string reverseStr(string s, int k) 42 | { 43 | int pos = 0; 44 | int n = s.size(); 45 | while (pos < n) 46 | { 47 | if (pos + k < n) 48 | { 49 | reverse(s.begin() + pos, s.begin() + pos + k); 50 | } 51 | else 52 | { 53 | reverse(s.begin() + pos, s.end()); 54 | } 55 | pos += 2 * k; 56 | } 57 | return s; 58 | } 59 | }; -------------------------------------------------------------------------------- /算法学习/5.String/reverse-string.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file reverse-string.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-26 7 | * 5.1 翻转字符串 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | 12 | #include 13 | using namespace std; 14 | 15 | class Solution 16 | { 17 | public: 18 | void reverseString(vector &s) 19 | { 20 | for (int i = 0, j = s.size() - 1; i < j; i++, j--) 21 | { 22 | swap(s[i], s[j]); 23 | } 24 | } 25 | }; 26 | 27 | class Solution 28 | { 29 | public: 30 | void reverseString(vector &s) 31 | { 32 | for (int i = 0, j = s.size() - 1; i < s.size() / 2; i++, j--) 33 | { 34 | swap(s[i], s[j]); 35 | } 36 | } 37 | }; -------------------------------------------------------------------------------- /算法学习/5.String/ti-huan-kong-ge-lcof.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file ti-huan-kong-ge-lcof.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-26 7 | * 5.3 替换空格 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | 12 | #include 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | string replaceSpace(string s) 20 | { 21 | // step 1 统计空格个数 22 | int count = 0; 23 | int oldi = s.size() - 1; // 原字符串最后一个元素 24 | for (int i = 0; i < s.size(); i++) 25 | { 26 | if (s[i] == ' ') 27 | { 28 | count++; 29 | } 30 | } 31 | // step 2 扩充数组 32 | s.resize(s.size() + count * 2); 33 | int newi = s.size() - 1; // 扩充后数组的最后一个元素 34 | // step 3 双指针从后往前替换元素 35 | while (oldi < newi) 36 | { 37 | if (s[oldi] == ' ') 38 | { 39 | s[newi] = '0'; 40 | s[newi - 1] = '2'; 41 | s[newi - 2] = '%'; 42 | newi -= 3; 43 | oldi--; 44 | } 45 | else 46 | { 47 | s[newi] = s[oldi]; 48 | newi--; 49 | oldi--; 50 | } 51 | } 52 | return s; 53 | } 54 | }; 55 | -------------------------------------------------------------------------------- /算法学习/5.String/zuo-xuan-zhuan-zi-fu-chuan-lcof.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file zuo-xuan-zhuan-zi-fu-chuan-lcof.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-26 7 | * 5.5 左旋字符串 8 | * - 主要是解题思路 9 | * 首先翻转部分 10 | * 然后翻转整体 11 | * @copyright Copyright (c) 2022 12 | * 13 | */ 14 | #include 15 | #include 16 | using namespace std; 17 | 18 | class Solution 19 | { 20 | public: 21 | string reverseLeftWords(string s, int n) 22 | { 23 | // step 1 翻转[0,n) 24 | reverse(s.begin(), s.begin() + n); 25 | // step 2 翻转[n,s.size()) 26 | reverse(s.begin() + n, s.end()); 27 | // step 3 翻转[0,s.size()) 28 | reverse(s.begin(), s.end()); 29 | return s; 30 | } 31 | }; -------------------------------------------------------------------------------- /算法学习/6.DoublePointer/remove-element.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file remove-element.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-05 7 | * 移除元素 8 | * - 双指针移除制定元素 9 | * @copyright Copyright (c) 2022 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | class Solution 18 | { 19 | public: 20 | int removeElement(vector &nums, int val) 21 | { 22 | int j = 0; 23 | for (int i = 0; i < nums.size(); i++) 24 | { 25 | if (nums[i] != val) 26 | { 27 | nums[j] = nums[i]; 28 | j++; 29 | } 30 | } 31 | nums.resize(j); 32 | return j; 33 | } 34 | }; 35 | 36 | int main(int argc, char **argv) 37 | { 38 | int n; 39 | cout << "输入数组数组大小 n" << std::endl; 40 | cin >> n; 41 | 42 | vector nums(n); 43 | int val; 44 | cout << "输入n个数字作为原数组" << std::endl; 45 | for (int i = 0; i < n; i++) 46 | { 47 | cin >> nums[i]; 48 | } 49 | cout << "输入删除数字" << std::endl; 50 | cin >> val; 51 | 52 | Solution solution; 53 | 54 | std::cout << "原来数组:" << std::endl; 55 | for (int i = 0; i < nums.size(); i++) 56 | { 57 | cout << nums[i] << " "; 58 | } 59 | std::cout << endl 60 | << "新的数组大小:" << solution.removeElement(nums, val) << std::endl; 61 | std::cout << "新的数组:" << std::endl; 62 | for (int i = 0; i < nums.size(); i++) 63 | { 64 | cout << nums[i] << " "; 65 | } 66 | std::cout << std::endl; 67 | 68 | cout << "-----结束-----" << endl; 69 | return 0; 70 | } -------------------------------------------------------------------------------- /算法学习/6.DoublePointer/reverse-string.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file reverse-string.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-26 7 | * 翻转字符串 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | 12 | #include 13 | using namespace std; 14 | 15 | class Solution0 16 | { 17 | public: 18 | void reverseString(vector &s) 19 | { 20 | //终止条件双指针相遇 21 | for (int i = 0, j = s.size() - 1; i < j; i++, j--) 22 | { 23 | swap(s[i], s[j]); 24 | } 25 | } 26 | }; 27 | 28 | class Solution1 29 | { 30 | public: 31 | void reverseString(vector &s) 32 | { 33 | //终止条件 从0开始的指针到达 size / 2 34 | for (int i = 0, j = s.size() - 1; i < s.size() / 2; i++, j--) 35 | { 36 | swap(s[i], s[j]); 37 | } 38 | } 39 | }; -------------------------------------------------------------------------------- /算法学习/7.StackQueue/evaluate-reverse-polish-notation.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file evaluate-reverse-polish-notation.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-28 7 | * https://leetcode.cn/problems/evaluate-reverse-polish-notation/ 8 | * 逆波兰表达式求值 中等 9 | * 这个题目难度主要是题目的理解 10 | * 就是先放入两个操作数, 11 | * 完后放入运算符号, 12 | * 则对先前的两个操作数进行二元运算 13 | * 运算结果作为新的操作数放入 14 | * @copyright Copyright (c) 2022 15 | * 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | using namespace std; 22 | 23 | class Solution 24 | { 25 | public: 26 | int evalRPN(vector &tokens) 27 | { 28 | stack st; 29 | for (int i = 0; i < tokens.size(); i++) 30 | { 31 | // 如果是运算符,取出2个数字并计算然后放入栈 32 | if (tokens[i] == "+" || 33 | tokens[i] == "-" || 34 | tokens[i] == "*" || 35 | tokens[i] == "/") 36 | { 37 | // num1 + - * / num2 38 | // 栈取出的第一个数字是num2 39 | long long num2 = st.top(); 40 | st.pop(); 41 | long long num1 = st.top(); 42 | st.pop(); 43 | if (tokens[i] == "+") 44 | st.push(num1 + num2); 45 | if (tokens[i] == "-") 46 | st.push(num1 - num2); 47 | if (tokens[i] == "*") 48 | st.push(num1 * num2); 49 | if (tokens[i] == "/") 50 | st.push(num1 / num2); 51 | } 52 | // 如果不是运算符,直接放入数字 53 | else 54 | { 55 | st.push(stoll(tokens[i])); // 字符串转换为数字 56 | } 57 | } 58 | 59 | int result = st.top(); 60 | st.pop(); 61 | return result; 62 | } 63 | }; -------------------------------------------------------------------------------- /算法学习/7.StackQueue/implement-queue-using-stacks.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file implement-queue-using-stacks.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-28 7 | * 用栈实现队列 8 | * @copyright Copyright (c) 2022 9 | * 10 | */ 11 | #include 12 | using namespace std; 13 | 14 | class MyQueue 15 | { 16 | public: 17 | stack stIn; 18 | stack stOut; 19 | 20 | MyQueue() 21 | { 22 | } 23 | 24 | // 从队列尾部加入元素 25 | void push(int x) 26 | { 27 | stIn.push(x); 28 | } 29 | 30 | // 从队列首移除元素 31 | int pop() 32 | { 33 | // 从stIn倒出来放入stOut 34 | if (stOut.empty()) 35 | { 36 | while (!stIn.empty()) 37 | { 38 | stOut.push(stIn.top()); 39 | stIn.pop(); 40 | } 41 | } 42 | 43 | int result = stOut.top(); 44 | stOut.pop(); 45 | return result; 46 | } 47 | 48 | // 返回队列首部的元素 49 | int peek() 50 | { 51 | int res = this->pop(); // 弹出stOut的第一个 52 | stOut.push(res); // 完事在放回stOut 53 | return res; 54 | } 55 | 56 | // 返回队列是否为空 57 | bool empty() 58 | { 59 | // stIn和stOut都为空则队列为空 60 | return stIn.empty() && stOut.empty(); 61 | } 62 | }; 63 | 64 | /** 65 | * Your MyQueue object will be instantiated and called as such: 66 | * MyQueue* obj = new MyQueue(); 67 | * obj->push(x); 68 | * int param_2 = obj->pop(); 69 | * int param_3 = obj->peek(); 70 | * bool param_4 = obj->empty(); 71 | */ -------------------------------------------------------------------------------- /算法学习/7.StackQueue/implement-stack-using-queues.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file implement-stack-using-queues.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-28 7 | * 使用队列实现栈 8 | * https://leetcode.cn/problems/implement-stack-using-queues/ 9 | * @copyright Copyright (c) 2022 10 | * 11 | */ 12 | #include 13 | using namespace std; 14 | class MyStack 15 | { 16 | public: 17 | queue que1; // 用来存储元素 18 | queue que2; // 辅助用来取到que1剩下一个 19 | 20 | MyStack() 21 | { 22 | } 23 | 24 | void push(int x) 25 | { 26 | que1.push(x); 27 | } 28 | 29 | int pop() 30 | { 31 | // que1:a b c que2:null 32 | // que1:c que2:a b 33 | // que1和que2来回倒一下 34 | int size = que1.size() - 1; 35 | while (size--) 36 | { 37 | que2.push(que1.front()); 38 | que1.pop(); 39 | } 40 | 41 | // 取出que1剩下的那一个 42 | int result = que1.front(); 43 | que1.pop(); 44 | 45 | // 再把que2放入que1 46 | // que1:a b que2:null 47 | que1 = que2; 48 | while (!que2.empty()) 49 | { 50 | que2.pop(); 51 | } 52 | 53 | // 返回结果 54 | return result; 55 | } 56 | 57 | int top() 58 | { 59 | // 队列的最后一个就是栈的顶部第一个 60 | return que1.back(); 61 | } 62 | 63 | bool empty() 64 | { 65 | return que1.empty(); 66 | } 67 | }; 68 | 69 | /** 70 | * Your MyStack object will be instantiated and called as such: 71 | * MyStack* obj = new MyStack(); 72 | * obj->push(x); 73 | * int param_2 = obj->pop(); 74 | * int param_3 = obj->top(); 75 | * bool param_4 = obj->empty(); 76 | */ -------------------------------------------------------------------------------- /算法学习/7.StackQueue/remove-all-adjacent-duplicates-in-string.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file remove-all-adjacent-duplicates-in-string.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-28 7 | * https://leetcode.cn/problems/remove-all-adjacent-duplicates-in-string/ 8 | * 删除字符串中的所有相邻重复项 9 | * @copyright Copyright (c) 2022 10 | * 11 | */ 12 | #include 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | class Solution0 18 | { 19 | public: 20 | string removeDuplicates(string s) 21 | { 22 | // 使用栈消除相同的元素 23 | stack st; 24 | for (char x : s) 25 | { 26 | if (st.empty() || st.top() != x) 27 | { 28 | st.push(x); 29 | } 30 | else 31 | { 32 | st.pop(); 33 | } 34 | } 35 | // 取出来放入string 36 | string result; 37 | while (!st.empty()) 38 | { 39 | result.push_back(st.top()); 40 | st.pop(); 41 | } 42 | // 反转一下字符串 43 | reverse(result.begin(), result.end()); 44 | return result; 45 | } 46 | }; 47 | 48 | class Solution1 49 | { 50 | public: 51 | string removeDuplicates(string s) 52 | { 53 | string result; 54 | for (char x : s) 55 | { 56 | if (result.empty() || result.back() != x) 57 | { 58 | // 放入不同的元素 59 | result.push_back(x); 60 | } 61 | else 62 | { 63 | // 消除相同的元素 64 | result.pop_back(); 65 | } 66 | } 67 | return result; 68 | } 69 | }; -------------------------------------------------------------------------------- /算法学习/7.StackQueue/sliding-window-maximum.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file sliding-window-maximum.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-28 7 | * https://leetcode.cn/problems/sliding-window-maximum/ 8 | * 滑动窗口最大值 9 | * sliding-window-maximum 10 | * @copyright Copyright (c) 2022 11 | * 12 | */ 13 | 14 | #include 15 | #include 16 | using namespace std; 17 | 18 | class Solution 19 | { 20 | private: 21 | // 自定一个单调队列从大到小 保存了最大(包括N个相等的)之后递减的序列 22 | class MyQueue 23 | { 24 | public: 25 | deque que; 26 | void pop(int value) 27 | { 28 | // 需要弹出的value等于队列中的最大值 29 | if (!que.empty() && value == que.front()) 30 | que.pop_front(); 31 | } 32 | void push(int value) 33 | { 34 | // 保存了最大(包括N个相等的) 35 | // 把que中小于value的全部删除 36 | while (!que.empty() && value > que.back()) 37 | { 38 | que.pop_back(); 39 | } 40 | // 在que大于等于vale的后边添加value 41 | que.push_back(value); 42 | } 43 | 44 | // 取出最大值,即队列第一个 45 | int max() 46 | { 47 | return que.front(); 48 | } 49 | }; 50 | 51 | public: 52 | vector maxSlidingWindow(vector &nums, int k) 53 | { 54 | MyQueue que; 55 | // 初始化先把第一个划窗的k个数放进去 56 | for (int i = 0; i < k; i++) 57 | { 58 | que.push(nums[i]); 59 | } 60 | // 第一个划窗的k个数的最大值 61 | vector result; 62 | result.push_back(que.max()); 63 | for (int i = k; i < nums.size(); i++) 64 | { 65 | que.pop(nums[i - k]); // i-k移出划窗 66 | que.push(nums[i]); // i加入划窗 67 | result.push_back(que.max()); 68 | } 69 | return result; 70 | } 71 | }; -------------------------------------------------------------------------------- /算法学习/7.StackQueue/top-k-frequent-elements.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file top-k-frequent-elements.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-28 7 | * https://leetcode.cn/problems/top-k-frequent-elements/ 8 | * 前 K 个高频元素 9 | * 返回高频元素可以按照任意顺序 10 | * @copyright Copyright (c) 2022 11 | * 12 | */ 13 | #include 14 | #include 15 | #include 16 | using namespace std; 17 | 18 | class Solution 19 | { 20 | public: 21 | // 自定义比较器,从大到小排列,小根堆 22 | class MyComparison 23 | { 24 | public: 25 | bool operator()(const pair &left, const pair &right) 26 | { 27 | return left.second > right.second; 28 | } 29 | }; 30 | 31 | vector topKFrequent(vector &nums, int k) 32 | { 33 | // step 1 统计元素出现个数 34 | // key:元素 ,value:出现次数 35 | unordered_map map; 36 | for (int i = 0; i < nums.size(); i++) 37 | { 38 | map[nums[i]]++; 39 | } 40 | // step 2 使用小根堆求解 41 | priority_queue, vector>, MyComparison> pri_que; 42 | for (unordered_map::iterator it = map.begin(); it != map.end(); it++) 43 | { 44 | pri_que.push(*it); 45 | if (pri_que.size() > k) 46 | { 47 | pri_que.pop(); // 小根堆弹出最小值 48 | } 49 | } 50 | // step 3 从堆里取出结果放入vector 51 | vector result; 52 | for (int i = 0; i < k; i++) 53 | { 54 | result.push_back(pri_que.top().first); 55 | pri_que.pop(); 56 | } 57 | return result; 58 | } 59 | }; -------------------------------------------------------------------------------- /算法学习/7.StackQueue/valid-parentheses.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file valid-parentheses.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2022-12-28 7 | * 20. 有效的括号 8 | * https://leetcode.cn/problems/valid-parentheses/ 9 | * 10 | * @copyright Copyright (c) 2022 11 | * 12 | */ 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | class Solution 18 | { 19 | public: 20 | bool isValid(string s) 21 | { 22 | if (s.size() % 2 != 0) 23 | { 24 | return false; 25 | } 26 | stack st; 27 | for (int i = 0; i < s.size(); i++) 28 | { 29 | if (s[i] == '(') 30 | st.push(')'); 31 | else if (s[i] == '{') 32 | st.push('}'); 33 | else if (s[i] == '[') 34 | st.push(']'); 35 | // 第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号 return false 36 | // 第二种情况:遍历字符串匹配的过程中,发现栈里没有我们要匹配的字符。所以return false 37 | else if (st.empty() || st.top() != s[i]) 38 | return false; 39 | else 40 | st.pop(); // st.top() 与 s[i]相等,栈弹出元素 41 | } 42 | // 第一种情况:此时我们已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false,否则就return true 43 | return st.empty(); 44 | } 45 | }; -------------------------------------------------------------------------------- /算法学习/7.StackQueue/栈与队列.md: -------------------------------------------------------------------------------- 1 | # 栈与队列 2 | 3 | ## 栈 4 | 元素**先进后出** 5 | 不提供访问与迭代器(iterator)遍历 6 | 提供push、pop等接口 7 | STL中栈往往不被归类为容器,而被归类为container adapter(容器适配器) 8 | ```c++ 9 | std::stack > third; // 使用vector为底层容器的 10 | ``` 11 | ## 队列 12 | 元素**先进先出** 13 | STL 队列也不被归类为容器,而被归类为container adapter( 容器适配器) 14 | ```c++ 15 | std::queue> third; // 定义以list为底层容器的队列 16 | ``` -------------------------------------------------------------------------------- /算法学习/8.BinaryTree/find-bottom-left-tree-value.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file find-bottom-left-tree-value.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-04 7 | * 8 | * @copyright Copyright (c) 2023 9 | * 10 | */ 11 | #include 12 | using namespace std; 13 | 14 | struct TreeNode 15 | { 16 | int val; 17 | TreeNode *left; 18 | TreeNode *right; 19 | TreeNode() : val(0), left(nullptr), right(nullptr) {} 20 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 21 | TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 22 | }; 23 | 24 | // 暴力递归 25 | class Solution { 26 | public: 27 | pair findBottomLeftValueRe(TreeNode *node) 28 | { 29 | //base case 30 | if(node->left == nullptr && node->right == nullptr) 31 | return make_pair(1,node->val); 32 | 33 | pair left ,right; 34 | if(node->left != nullptr) 35 | { 36 | left = findBottomLeftValueRe(node->left); 37 | } 38 | if(node->right != nullptr) 39 | { 40 | right = findBottomLeftValueRe(node->right); 41 | } 42 | if(left.first>=right.first) 43 | return make_pair(left.first+1,left.second); 44 | else 45 | return make_pair(right.first+1,right.second); 46 | 47 | } 48 | 49 | int findBottomLeftValue(TreeNode* root) { 50 | return findBottomLeftValueRe(root).second; 51 | 52 | } 53 | }; -------------------------------------------------------------------------------- /算法学习/8.BinaryTree/lowest-common-ancestor-of-a-binary-tree.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lowest-common-ancestor-of-a-binary-tree.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-10 7 | * 236. 二叉树的最近公共祖先 8 | * https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | using namespace std; 13 | 14 | // 二叉树的定义 15 | struct TreeNode 16 | { 17 | int val; 18 | TreeNode *left; 19 | TreeNode *right; 20 | TreeNode() : val(0), left(nullptr), right(nullptr) {} 21 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 22 | TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 23 | }; 24 | 25 | class Solution { 26 | public: 27 | // 前提是 p,q 一定都是树中的节点 28 | // 函数返回值为是p,q的其中之一的最近的根节点,更接近根节点的会把不接近的顶替掉 29 | TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) 30 | { 31 | //base case 32 | if(root == p ||root ==q ||root == nullptr) 33 | { 34 | return root; 35 | } 36 | 37 | TreeNode *left = lowestCommonAncestor(root->left,p,q); 38 | TreeNode *right = lowestCommonAncestor(root->right,p,q); 39 | if(left != nullptr && right != nullptr) 40 | return root; 41 | if(left == nullptr) 42 | return right; 43 | return left; 44 | 45 | } 46 | }; -------------------------------------------------------------------------------- /算法学习/8.BinaryTree/merge-two-binary-trees.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file merge-two-binary-trees.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-06 7 | * 617. 合并二叉树 8 | * https://leetcode.cn/problems/merge-two-binary-trees/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | // 二叉树的定义 17 | struct TreeNode 18 | { 19 | int val; 20 | TreeNode *left; 21 | TreeNode *right; 22 | TreeNode() : val(0), left(nullptr), right(nullptr) {} 23 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 24 | TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 25 | }; 26 | 27 | //暴力递归 28 | class Solution 29 | { 30 | public: 31 | TreeNode *mergeTrees(TreeNode *root1, TreeNode *root2) 32 | { 33 | // base case 34 | if (root1 == nullptr && root2 == nullptr) 35 | return nullptr; 36 | 37 | TreeNode *root; 38 | if (root1 == nullptr) 39 | { 40 | root = root2; 41 | root->left = mergeTrees(nullptr, root2->left); 42 | root->right = mergeTrees(nullptr, root2->right); 43 | return root; 44 | } 45 | if (root2 == nullptr) 46 | { 47 | root = root1; 48 | root->left = mergeTrees(root1->left, nullptr); 49 | root->right = mergeTrees(root1->right, nullptr); 50 | return root; 51 | } 52 | 53 | root = root1; 54 | root->val = root1->val + root2->val; 55 | root->left = mergeTrees(root1->left, root2->left); 56 | root->right = mergeTrees(root1->right, root2->right); 57 | delete root2; 58 | 59 | return root; 60 | } 61 | }; 62 | -------------------------------------------------------------------------------- /算法学习/8.BinaryTree/path-sum.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file path-sum.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-04 7 | * 112.路径总和 8 | * https://leetcode.cn/problems/path-sum/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | using namespace std; 14 | 15 | // 二叉树的定义 16 | struct TreeNode 17 | { 18 | int val; 19 | TreeNode *left; 20 | TreeNode *right; 21 | TreeNode() : val(0), left(nullptr), right(nullptr) {} 22 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 23 | TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 24 | }; 25 | 26 | // 暴力递归 27 | class Solution { 28 | public: 29 | bool hasPathSum(TreeNode* root, int targetSum) 30 | { 31 | if(root == nullptr) 32 | return false; 33 | 34 | // base case 35 | targetSum = targetSum - root->val; 36 | if(root->left == nullptr && root->right == nullptr && targetSum == 0) 37 | return true; 38 | 39 | 40 | if(hasPathSum(root->left,targetSum)) 41 | return true; 42 | 43 | 44 | if(hasPathSum(root->right,targetSum)) 45 | return true; 46 | 47 | return false; 48 | } 49 | }; -------------------------------------------------------------------------------- /算法学习/8.BinaryTree/二叉树.md: -------------------------------------------------------------------------------- 1 | # 二叉树 2 | ## 分类 3 | ### 满二叉树 4 | 如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。 5 | $k$个层,$k$从0开始 6 | $2^k+1$个节点 7 | ### 完全二叉树 8 | 除了最底层节点可能没填满外,其余每层节点数都达到最大值。 9 | 最下面一层的节点都集中在该层最左边的若干位置。 10 | 若最底层为第$h$层,则该层包含 $[1,2^h]$ 个节点。 11 | ### 二叉搜索树 12 | 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 13 | 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 14 | 它的左、右子树也分别为二叉排序树。 15 | ### 平衡二叉树 16 | 它是一棵空树或它的左右两个子树的高度差的绝对值不超过1 17 | 18 | ## 遍历 19 | ### 深度优先 20 | 1. 前序遍历(递归法,迭代法)递归序中保存头(当前),递归左子节点,递归右子节点 21 | 2. 中序遍历(递归法,迭代法)递归左子节点,递归序中保存头(当前),递归右子节点 22 | 3. 后序遍历(递归法,迭代法)递归左子节点,递归右子节点,递归序中保存头(当前) 23 | > 前中后主要关注的是 头节点弹出存储在**递归调序**用左右子节点的相对位置 24 | ### 广度优先 25 | 层次遍历(迭代法) 26 | 27 | ## 套路 28 | ### 递归 29 | 假设可以从二叉树的左右节点获取信息 30 | 1. 分析各种情况 31 | 1. 左树情况 32 | 2. 右树情况 33 | 3. 左右比较、合并情况 34 | 2. 设法获取左右树的信息 35 | 3. 写递归函数 -------------------------------------------------------------------------------- /算法学习/8.BinaryTree/二叉树深度/maximum-depth-of-n-ary-tree.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file maximum-depth-of-n-ary-tree.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-01-13 7 | * N 叉树的最大深度 8 | * 最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。 9 | * N 叉树输入按层序遍历序列化表示,每组子节点由空值分隔(请参见示例)。 10 | * @copyright Copyright (c) 2023 11 | * 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | using namespace std; 20 | 21 | // N叉树的定义 22 | class Node 23 | { 24 | public: 25 | int val; 26 | vector children; 27 | 28 | Node() {} 29 | 30 | Node(int _val) 31 | { 32 | val = _val; 33 | } 34 | 35 | Node(int _val, vector _children) 36 | { 37 | val = _val; 38 | children = _children; 39 | } 40 | }; 41 | 42 | // 递归方式 43 | class Solution0 44 | { 45 | public: 46 | int getDepth(Node *node) 47 | { 48 | if (node == NULL) 49 | return 0; 50 | int depth = 0; 51 | for (int i = 0; i < node->children.size(); i++) 52 | { 53 | depth = max(depth, getDepth(node->children[i])); 54 | } 55 | return depth + 1; 56 | } 57 | int maxDepth(Node *root) 58 | { 59 | return getDepth(root); 60 | } 61 | }; 62 | 63 | // 层序遍历迭代 64 | class Solution0 65 | { 66 | public: 67 | int maxDepth(Node *root) 68 | { 69 | int depth = 0; 70 | queue que; 71 | if (root != NULL) 72 | que.push(root); 73 | while (!que.empty()) 74 | { 75 | int size = que.size(); 76 | depth++; 77 | for (int i = 0; i < size; i++) 78 | { 79 | Node *node = que.front(); 80 | que.pop(); 81 | for (int j = 0; j < node->children.size(); j++) 82 | { 83 | que.push(node->children[j]); 84 | } 85 | } 86 | } 87 | return depth; 88 | } 89 | }; -------------------------------------------------------------------------------- /算法学习/8.BinaryTree/前中后序遍历/uccessor-lcci.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file uccessor-lcci.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-04-20 7 | * 后继节点 8 | * https://leetcode.cn/problems/successor-lcci/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | // 二叉树的定义 18 | struct TreeNode 19 | { 20 | int val; 21 | TreeNode *left; 22 | TreeNode *right; 23 | TreeNode() : val(0), left(nullptr), right(nullptr) {} 24 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 25 | TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 26 | }; 27 | 28 | // 直接把后序遍历求出来,然后取寻找,没注意题目说的是二叉搜索树算了 29 | class Solution { 30 | public: 31 | void inorder(TreeNode* root, vector &result) 32 | { 33 | if(root->left!=nullptr) 34 | inorder(root->left,result); 35 | result.push_back(root); 36 | if(root->right!=nullptr) 37 | inorder(root->right,result); 38 | 39 | } 40 | 41 | TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) 42 | { 43 | vector result; 44 | inorder(root,result); 45 | for(int i = 0;i 6 | #include 7 | #include 8 | using namespace std; 9 | ``` 10 | bottom-up 使用N个无序的数从叶子到根节点建立堆结构,时间复杂度为 11 | top-down 使用N个无序的数从根到叶子节点建立堆结构,时间复杂度为$O(N*logN)$ 12 | ## 堆的操作 13 | ```c++ 14 | //取出操作 15 | //将根位置取出,heapsize-1;并用最后一个位置的值放到根位置 16 | //对头节点做heapify(),如果存在子节点大于该节点将该节点与子节点中较大的交换,继续判断 17 | void heapify();//从给定位置,调整当前节点为根节点的堆结构,用在高度为h的节点上的时间为O(h) 18 | //插入操作 19 | //插入到最后一个位置,如果该节点比父节点大则交换,继续判断 20 | void heapinsert();//插入到最后,然后调整当前节点为叶子节点的堆结构 21 | ``` 22 | 23 | ## 大根堆 24 | ```c++ 25 | pririty_queue,less> myQue; 26 | ``` 27 | ## 小根堆 28 | ```c++ 29 | pririty_queue, greater> myQue; 30 | ``` -------------------------------------------------------------------------------- /算法学习/8.BinaryTree/层序遍历/average-of-levels-in-binary-tree.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file average-of-levels-in-binary-tree.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-01-04 7 | * https://leetcode.cn/problems/average-of-levels-in-binary-tree/ 8 | * 二叉树的层平均值 9 | * @copyright Copyright (c) 2023 10 | * 给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10e-5 以内的答案可以被接受。 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | using namespace std; 18 | 19 | // 二叉树的定义 20 | struct TreeNode 21 | { 22 | int val; 23 | TreeNode *left; 24 | TreeNode *right; 25 | TreeNode() : val(0), left(nullptr), right(nullptr) {} 26 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 27 | TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 28 | }; 29 | 30 | class Solution 31 | { 32 | public: 33 | vector averageOfLevels(TreeNode *root) 34 | { 35 | queue que; 36 | vector result; 37 | if (root != NULL) 38 | que.push(root); 39 | while (!que.empty()) 40 | { 41 | int size = que.size(); 42 | double sum = 0; // 统计每一层的和 43 | for (int i = 0; i < size; i++) 44 | { 45 | TreeNode *node = que.front(); 46 | que.pop(); 47 | sum += node->val; // 求和 48 | if (node->left) 49 | que.push(node->left); 50 | if (node->right) 51 | que.push(node->right); 52 | } 53 | result.push_back(sum / size); // 求均值 54 | } 55 | return result; 56 | } 57 | }; -------------------------------------------------------------------------------- /算法学习/8.BinaryTree/层序遍历/binary-tree-level-order-traversal-ii.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file binary-tree-level-order-traversal-ii.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-01-04 7 | * https://leetcode.cn/problems/binary-tree-level-order-traversal-ii/ 8 | * 二叉树的层序遍历II 9 | * 给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历) 10 | * @copyright Copyright (c) 2023 11 | * 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | using namespace std; 19 | 20 | // 二叉树的定义 21 | struct TreeNode 22 | { 23 | int val; 24 | TreeNode *left; 25 | TreeNode *right; 26 | TreeNode() : val(0), left(nullptr), right(nullptr) {} 27 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 28 | TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 29 | }; 30 | class Solution 31 | { 32 | public: 33 | vector> levelOrderBottom(TreeNode *root) 34 | { 35 | queue que; 36 | vector> result; 37 | if (root != NULL) 38 | que.push(root); 39 | while (!que.empty()) 40 | { 41 | int size = que.size(); 42 | vector vec; 43 | for (int i = 0; i < size; i++) 44 | { 45 | TreeNode *node = que.front(); 46 | que.pop(); 47 | if (node->left) 48 | que.push(node->left); 49 | if (node->right) 50 | que.push(node->right); 51 | vec.push_back(node->val); 52 | } 53 | result.push_back(vec); 54 | } 55 | reverse(result.begin(), result.end()); 56 | return result; 57 | } 58 | }; -------------------------------------------------------------------------------- /算法学习/8.BinaryTree/层序遍历/binary-tree-level-order-traversal.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file binary-tree-level-order-traversal.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-01-04 7 | * https://leetcode.cn/problems/binary-tree-level-order-traversal/ 8 | * 二叉树层序遍历 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | #include 14 | using namespace std; 15 | 16 | // 二叉树的定义 17 | struct TreeNode 18 | { 19 | int val; 20 | TreeNode *left; 21 | TreeNode *right; 22 | TreeNode() : val(0), left(nullptr), right(nullptr) {} 23 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 24 | TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 25 | }; 26 | 27 | class Solution 28 | { 29 | public: 30 | vector> levelOrder(TreeNode *root) 31 | { 32 | queue que; 33 | vector> result; 34 | if (root != NULL) 35 | que.push(root); 36 | 37 | while (!que.empty()) 38 | { 39 | int size = que.size(); // 当前层的的size 40 | vector vec; // 当前层的节点 41 | for (int i = 0; i < size; i++) 42 | { 43 | //取出当前层的所有节 44 | TreeNode *node = que.front(); 45 | que.pop(); 46 | vec.push_back(node->val); 47 | //保存下一层的所有节点 48 | if (node->left) 49 | que.push(node->left); 50 | if (node->right) 51 | que.push(node->right); 52 | } 53 | result.push_back(vec); 54 | } 55 | return result; 56 | } 57 | }; -------------------------------------------------------------------------------- /算法学习/8.BinaryTree/层序遍历/binary-tree-right-side-view.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file binary-tree-right-side-view.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-01-04 7 | * https://leetcode.cn/problems/binary-tree-right-side-view/ 8 | * 二叉树的右视图 9 | * 给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。 10 | * @copyright Copyright (c) 2023 11 | * 12 | */ 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | // 二叉树的定义 18 | struct TreeNode 19 | { 20 | int val; 21 | TreeNode *left; 22 | TreeNode *right; 23 | TreeNode() : val(0), left(nullptr), right(nullptr) {} 24 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 25 | TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 26 | }; 27 | 28 | class Solution 29 | { 30 | public: 31 | vector rightSideView(TreeNode *root) 32 | { 33 | queue que; 34 | vector result; 35 | if (root != NULL) 36 | que.push(root); 37 | 38 | while (!que.empty()) 39 | { 40 | int size = que.size(); // 当前层的的size 41 | for (int i = 0; i < size; i++) 42 | { 43 | // 取出当前层的所有节 44 | TreeNode *node = que.front(); 45 | que.pop(); 46 | if (i == size - 1) // 当前从的最后一个节点就是最右侧的节点 47 | result.push_back(node->val); 48 | // 保存下一层的所有节点 49 | if (node->left) 50 | que.push(node->left); 51 | if (node->right) 52 | que.push(node->right); 53 | } 54 | } 55 | return result; 56 | } 57 | }; -------------------------------------------------------------------------------- /算法学习/8.BinaryTree/搜索二叉树/insert-into-a-binary-search-tree.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file insert-into-a-binary-search-tree.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-10 7 | * 701. 二叉搜索树中的插入操作 8 | * https://leetcode.cn/problems/insert-into-a-binary-search-tree/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | using namespace std; 14 | 15 | // 二叉树的定义 16 | struct TreeNode 17 | { 18 | int val; 19 | TreeNode *left; 20 | TreeNode *right; 21 | TreeNode() : val(0), left(nullptr), right(nullptr) {} 22 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 23 | TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 24 | }; 25 | 26 | //直接在叶子节点插入 27 | class Solution { 28 | public: 29 | TreeNode* insertIntoBST(TreeNode* root, int val) 30 | { 31 | if(root == nullptr) 32 | { 33 | TreeNode * node = new TreeNode(val); 34 | return node; 35 | } 36 | 37 | TreeNode * pre = nullptr; 38 | TreeNode * cur = root; 39 | TreeNode * node = new TreeNode(val); 40 | while(cur != nullptr) 41 | { 42 | pre = cur; 43 | if(valval) 44 | cur = cur->left; 45 | else 46 | cur =cur->right; 47 | } 48 | if(val < pre->val) 49 | pre->left = node; 50 | else 51 | pre->right = node; 52 | 53 | return root; 54 | 55 | } 56 | }; -------------------------------------------------------------------------------- /算法学习/8.BinaryTree/搜索二叉树/lowest-common-ancestor-of-a-binary-search-tree.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lowest-common-ancestor-of-a-binary-search-tree.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-10 7 | * 235. 二叉搜索树的最近公共祖先 8 | * https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | using namespace std; 13 | 14 | // 二叉树的定义 15 | struct TreeNode 16 | { 17 | int val; 18 | TreeNode *left; 19 | TreeNode *right; 20 | TreeNode() : val(0), left(nullptr), right(nullptr) {} 21 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 22 | TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 23 | }; 24 | 25 | 26 | class Solution { 27 | public: 28 | TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) 29 | { 30 | while(root != nullptr) 31 | { 32 | //分裂了直接返回 33 | if(p->val > root->val && q->val > root->val) 34 | root = root->right; 35 | else if(p->val < root->val && q->val < root->val) 36 | root = root->left; 37 | else 38 | return root; 39 | } 40 | return root; 41 | 42 | } 43 | }; -------------------------------------------------------------------------------- /算法学习/8.BinaryTree/搜索二叉树/minimum-absolute-difference-in-bst.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file minimum-absolute-difference-in-bst.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-10 7 | * 530. 二叉搜索树的最小绝对差 8 | * https://leetcode.cn/problems/minimum-absolute-difference-in-bst/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | // 二叉树的定义 18 | struct TreeNode 19 | { 20 | int val; 21 | TreeNode *left; 22 | TreeNode *right; 23 | TreeNode() : val(0), left(nullptr), right(nullptr) {} 24 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 25 | TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 26 | }; 27 | 28 | // 中序遍历迭代法 29 | class Solution 30 | { 31 | public: 32 | int getMinimumDifference(TreeNode *root) 33 | { 34 | if (root == nullptr) 35 | return 0; 36 | 37 | int result = INT_MAX; 38 | stack mySt; 39 | TreeNode *pre = nullptr; 40 | TreeNode *cur = root; 41 | 42 | // 中序遍历 43 | while (cur != nullptr || !mySt.empty()) 44 | { 45 | if (cur != nullptr) 46 | { 47 | mySt.push(cur); 48 | cur = cur->left; 49 | } 50 | else 51 | { 52 | cur = mySt.top(); 53 | mySt.pop(); 54 | if (pre != nullptr && cur->val - pre->val < result) 55 | { 56 | result = cur->val - pre->val; 57 | } 58 | pre = cur; 59 | cur = cur->right; 60 | } 61 | } 62 | 63 | return result; 64 | } 65 | }; -------------------------------------------------------------------------------- /算法学习/8.BinaryTree/搜索二叉树/search-in-a-binary-search-tree.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file search-in-a-binary-search-tree.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-06 7 | * 700. 二叉搜索树中的搜索 8 | * https://leetcode.cn/problems/search-in-a-binary-search-tree/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | // 二叉树的定义 17 | struct TreeNode 18 | { 19 | int val; 20 | TreeNode *left; 21 | TreeNode *right; 22 | TreeNode() : val(0), left(nullptr), right(nullptr) {} 23 | TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 24 | TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 25 | }; 26 | 27 | // 暴力递归 28 | class Solution 29 | { 30 | public: 31 | TreeNode *searchBST(TreeNode *root, int val) 32 | { 33 | // base case 34 | if (root == nullptr) 35 | return nullptr; 36 | 37 | if (root->val == val) 38 | return root; 39 | 40 | if (root->val < val) 41 | return searchBST(root->right, val); 42 | else 43 | return searchBST(root->left, val); 44 | } 45 | }; 46 | 47 | // 迭代 48 | class Solution 49 | { 50 | public: 51 | TreeNode *searchBST(TreeNode *root, int val) 52 | { 53 | 54 | while (root != nullptr) 55 | { 56 | if (root->val == val) 57 | return root; 58 | if (root->val < val) 59 | root = root->right; 60 | else 61 | root = root->left; 62 | } 63 | return root; 64 | } 65 | }; -------------------------------------------------------------------------------- /算法学习/8.BinaryTree/搜索二叉树/trim-a-binary-search-tree: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2hanhan/algorithm/abe1ace7446ea6184b68e0e401ba940af7e194c8/算法学习/8.BinaryTree/搜索二叉树/trim-a-binary-search-tree -------------------------------------------------------------------------------- /算法学习/9.BacktrackingAlgorithm/combination-sum-ii.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file combination-sum-ii.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-10 7 | * 40. 组合总和 II 8 | * https://leetcode.cn/problems/combination-sum-ii/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | using namespace std; 16 | 17 | class Solution 18 | { 19 | public: 20 | vector> result; 21 | vector path; 22 | 23 | void backTracking(vector &candidates, int sum, int startIndex) 24 | { 25 | if (sum == 0) 26 | { 27 | result.push_back(path); 28 | return; 29 | } 30 | if (sum < 0) 31 | { 32 | return; 33 | } 34 | 35 | for (int i = startIndex; i < candidates.size(); i++) 36 | { 37 | if (i > startIndex && candidates[i] == candidates[i - 1])//去重操作,重复的数字只在第一次使用时进行相同数字的叠加 38 | continue; 39 | 40 | path.push_back(candidates[i]); 41 | sum -= candidates[i]; 42 | backTracking(candidates, sum, i + 1); 43 | path.pop_back(); 44 | sum += candidates[i]; 45 | } 46 | } 47 | 48 | vector> combinationSum2(vector &candidates, int target) 49 | { 50 | result.clear(); 51 | path.clear(); 52 | sort(candidates.begin(), candidates.end()); 53 | backTracking(candidates, target, 0); 54 | return result; 55 | } 56 | }; -------------------------------------------------------------------------------- /算法学习/9.BacktrackingAlgorithm/combination-sum-iii.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file combination-sum-iii.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-10 7 | * 216. 组合总和 III 8 | * https://leetcode.cn/problems/combination-sum-iii/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | vector> result; 20 | vector path; 21 | void backTracking(int k, int sum, int startIndex) 22 | { 23 | if (path.size() == k) 24 | { 25 | if (sum == 0) 26 | result.push_back(path); 27 | return; 28 | } 29 | // 求和以及不足 sum < i + 1 30 | if (sum < startIndex) 31 | return; 32 | 33 | // 个数不足 k - path.size() <= 9 - i +1 34 | for (int i = startIndex; k - path.size() <= 9 - i + 1; i++) 35 | { 36 | sum -= i; 37 | path.push_back(i); 38 | backTracking(k, sum, i + 1); 39 | sum += i; 40 | path.pop_back(); 41 | } 42 | 43 | // 不剪支 44 | // for (int i = startIndex; i <= 9; i++) 45 | // { 46 | // sum -= i; 47 | // path.push_back(i); 48 | // backTracking(k, sum, i + 1); 49 | // sum += i; 50 | // path.pop_back(); 51 | // } 52 | } 53 | vector> combinationSum3(int k, int n) 54 | { 55 | result.clear(); 56 | path.clear(); 57 | backTracking(k, n, 1); 58 | return result; 59 | } 60 | }; -------------------------------------------------------------------------------- /算法学习/9.BacktrackingAlgorithm/combination-sum.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file combination-sum.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-10 7 | * 39. 组合总和 8 | * https://leetcode.cn/problems/combination-sum/ 9 | * 题目条件应该是无重复的正整数数组 10 | * @copyright Copyright (c) 2023 11 | * 12 | */ 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | vector> result; 20 | vector path; 21 | 22 | void backTracking(vector &candidates, int sum, int startIndex) 23 | { 24 | if (sum == 0) 25 | { 26 | result.push_back(path); 27 | return; 28 | } 29 | if (sum < 0) 30 | { 31 | return; 32 | } 33 | 34 | for (int i = startIndex; i < candidates.size(); i++) 35 | { 36 | path.push_back(candidates[i]); 37 | sum -= candidates[i]; 38 | backTracking(candidates, sum, i); 39 | path.pop_back(); 40 | sum += candidates[i]; 41 | } 42 | } 43 | 44 | vector> combinationSum(vector &candidates, int target) 45 | { 46 | result.clear(); 47 | path.clear(); 48 | backTracking(candidates, target, 0); 49 | return result; 50 | } 51 | }; -------------------------------------------------------------------------------- /算法学习/9.BacktrackingAlgorithm/combinations.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file combinations.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-10 7 | * 77. 组合 8 | * https://leetcode.cn/problems/combinations/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | using namespace std; 14 | 15 | class Solution 16 | { 17 | public: 18 | vector> result; 19 | vector path; 20 | void backtracking(int n, int k, int startindex) 21 | { 22 | // base case 23 | if (path.size() == k) 24 | { 25 | result.push_back(path); 26 | return; 27 | } 28 | 29 | // 当前还需要 k-path.size()个元素才能凑够 k个 30 | // 还剩下 n+1-i个元素可以取 31 | for (int i = startindex; k - path.size() <= n + 1 -i; i++) 32 | { 33 | path.push_back(i); 34 | backtracking(n, k, i + 1); 35 | path.pop_back(); 36 | } 37 | } 38 | vector> combine(int n, int k) 39 | { 40 | result.clear(); 41 | path.clear(); 42 | backtracking(n, k, 1); 43 | return result; 44 | } 45 | }; -------------------------------------------------------------------------------- /算法学习/9.BacktrackingAlgorithm/letter-combinations-of-a-phone-number.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file letter-combinations-of-a-phone-number.cpp 3 | * 17. 电话号码的字母组合 4 | * @author your name (you@domain.com) 5 | * @brief 6 | * @version 0.1 7 | * @date 2023-05-10 8 | * https://leetcode.cn/problems/letter-combinations-of-a-phone-number/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | #include 14 | using namespace std; 15 | class Solution 16 | { 17 | public: 18 | const string letterMap[10] = { 19 | "", // 0 20 | "", // 1 21 | "abc", // 2 22 | "def", // 3 23 | "ghi", // 4 24 | "jkl", // 5 25 | "mno", // 6 26 | "pqrs", // 7 27 | "tuv", // 8 28 | "wxyz", // 9 29 | }; 30 | vector result; 31 | string path; 32 | 33 | void backTracking(string digits, int index) 34 | { 35 | if (path.size() == digits.size()) 36 | { 37 | result.push_back(path); 38 | return; 39 | } 40 | int digit = digits[index] - '0'; 41 | string letters = letterMap[digit]; 42 | for (int i = 0; i < letters.size(); i++) 43 | { 44 | path.push_back(letters[i]); 45 | backTracking(digits, index + 1); 46 | path.pop_back(); 47 | } 48 | } 49 | vector letterCombinations(string digits) 50 | { 51 | result.clear(); 52 | path.clear(); 53 | if (digits.empty()) 54 | return result; 55 | backTracking(digits, 0); 56 | return result; 57 | } 58 | }; -------------------------------------------------------------------------------- /算法学习/9.BacktrackingAlgorithm/non-decreasing-subsequences.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file non-decreasing-subsequences.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-12 7 | * 491. 递增子序列 8 | * https://leetcode.cn/problems/non-decreasing-subsequences/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | using namespace std; 17 | 18 | class Solution 19 | { 20 | public: 21 | vector> result; 22 | vector path; 23 | 24 | void backTracking(vector &nums, int startIndex) 25 | { 26 | if (path.size() >= 2) 27 | result.push_back(path); 28 | 29 | set have;//无排序的去重操作,以have为第一个的排序已经处理过了 30 | for (int i = startIndex; i < nums.size(); i++) 31 | { 32 | if (have.count(nums[i]) || (!path.empty() && nums[i] < path[path.size() - 1])) 33 | continue; 34 | 35 | path.push_back(nums[i]); 36 | have.insert(nums[i]); 37 | backTracking(nums, i + 1); 38 | path.pop_back(); 39 | } 40 | } 41 | 42 | vector> findSubsequences(vector &nums) 43 | { 44 | result.clear(); 45 | path.clear(); 46 | 47 | backTracking(nums, 0); 48 | return result; 49 | } 50 | }; -------------------------------------------------------------------------------- /算法学习/9.BacktrackingAlgorithm/permutations.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file permutations.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-12 7 | * 46. 全排列 8 | * https://leetcode.cn/problems/permutations/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | vector> result; 20 | vector path; 21 | void backTracking(vector &nums, vector &used) 22 | { 23 | if (path.size() == nums.size()) 24 | { 25 | result.push_back(path); 26 | return; 27 | } 28 | 29 | for (int i = 0; i < nums.size(); i++) 30 | { 31 | if (used[i]) 32 | continue; 33 | used[i] = true; 34 | path.push_back(nums[i]); 35 | backTracking(nums, used); 36 | used[i] = false; 37 | path.pop_back(); 38 | } 39 | } 40 | vector> permute(vector &nums) 41 | { 42 | result.clear(); 43 | path.clear(); 44 | vector used(nums.size(), false); 45 | backTracking(nums, used); 46 | return result; 47 | } 48 | }; -------------------------------------------------------------------------------- /算法学习/9.BacktrackingAlgorithm/restore-ip-addresses.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file restore-ip-addresses.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-12 7 | * 93. 复原 IP 地址 8 | * https://leetcode.cn/problems/restore-ip-addresses/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | #include 14 | 15 | using namespace std; 16 | 17 | class Solution 18 | { 19 | public: 20 | vector result; 21 | int pointSum; 22 | // [left,right] 23 | bool isValid(string &s, int left, int right) 24 | { 25 | if (left > right) 26 | return false; 27 | // 首个数字为0的多位数 28 | if (s[left] == '0' && left != right) 29 | return false; 30 | 31 | int num = 0; 32 | for (int i = left; i <= right; i++) 33 | { 34 | if (s[i] > '9' || s[i] < '0') 35 | return false; 36 | num = num * 10 + (s[i] - '0'); 37 | if (num > 255) 38 | return false; 39 | } 40 | 41 | return true; 42 | } 43 | 44 | void backTracking(string &s, int startIndex) 45 | { 46 | if (pointSum == 3) 47 | { 48 | if (isValid(s, startIndex, s.size() - 1)) 49 | { 50 | result.push_back(s); 51 | } 52 | } 53 | 54 | for (int i = startIndex; i < s.size() - 1; i++) 55 | { 56 | if (!isValid(s, startIndex, i)) 57 | return; 58 | s.insert(s.begin() + i + 1, '.'); 59 | pointSum++; 60 | backTracking(s, i + 2); 61 | s.erase(s.begin() + i + 1); 62 | pointSum--; 63 | } 64 | } 65 | 66 | vector restoreIpAddresses(string s) 67 | { 68 | result.clear(); 69 | pointSum = 0; 70 | backTracking(s, 0); 71 | return result; 72 | } 73 | }; -------------------------------------------------------------------------------- /算法学习/9.BacktrackingAlgorithm/subsets-ii.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file subsets-ii.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-12 7 | * 90. 子集 II 8 | * https://leetcode.cn/problems/subsets-ii/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | #include 14 | using namespace std; 15 | 16 | class Solution 17 | { 18 | public: 19 | vector> result; 20 | vector path; 21 | void backTracking(vector &nums, int startIndex) 22 | { 23 | result.push_back(path); 24 | if (startIndex == nums.size()) 25 | { 26 | return; 27 | } 28 | 29 | for (int i = startIndex; i < nums.size(); i++) 30 | { 31 | if (i > startIndex && nums[i] == nums[i - 1]) 32 | continue; 33 | path.push_back(nums[i]); 34 | backTracking(nums, i + 1); 35 | path.pop_back(); 36 | } 37 | } 38 | 39 | vector> subsetsWithDup(vector &nums) 40 | { 41 | result.clear(); 42 | path.clear(); 43 | sort(nums.begin(),nums.end()); 44 | backTracking(nums, 0); 45 | return result; 46 | } 47 | }; -------------------------------------------------------------------------------- /算法学习/9.BacktrackingAlgorithm/subsets.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file subsets.cpp 3 | * @author your name (you@domain.com) 4 | * @brief 5 | * @version 0.1 6 | * @date 2023-05-12 7 | * 78. 子集 8 | * https://leetcode.cn/problems/subsets/ 9 | * @copyright Copyright (c) 2023 10 | * 11 | */ 12 | #include 13 | using namespace std; 14 | 15 | class Solution 16 | { 17 | public: 18 | vector> result; 19 | vector path; 20 | void backTracking(vector &nums, int stratIndex) 21 | { 22 | // 取子集每个都结果都加入 23 | result.push_back(path); 24 | if (stratIndex == nums.size()) 25 | return; 26 | 27 | for (int i = stratIndex; i < nums.size(); i++) 28 | { 29 | path.push_back(nums[i]); 30 | backTracking(nums, i + 1); 31 | path.pop_back(); 32 | } 33 | } 34 | 35 | vector> subsets(vector &nums) 36 | { 37 | result.clear(); 38 | path.clear(); 39 | backTracking(nums, 0); 40 | return result; 41 | } 42 | }; 43 | 44 | -------------------------------------------------------------------------------- /算法学习/9.BacktrackingAlgorithm/回溯算法.md: -------------------------------------------------------------------------------- 1 | # 回溯算法 2 | > 类似暴力递归,只是需要恢复到之前的某个状态,弹出完后在加新的。 3 | > 主要应对的是循环次数是可变参数的问题,暴力递归的终止次数类似for循环的嵌套次数。 4 | 5 | ## 解决问题 6 | 1. 组合问题 7 | ```c++ 8 | void backTracking(vector nums, int stratIndex) 9 | { 10 | //终止条件 11 | if(stratIndex == nums.size()) 12 | { 13 | result.push_back(path); 14 | return; 15 | } 16 | 17 | for(int i = startIndex; i < nums.size(); i++) 18 | { 19 | //层内去重 20 | if() 21 | continue; 22 | path.push_back(nums[i]); 23 | backTracking(nums,i+1); 24 | path.pop_back(); 25 | } 26 | } 27 | 28 | ``` 29 | 2. 子集问题 30 | ```c++ 31 | 32 | void backTracking(vector nums, int stratIndex) 33 | { 34 | // 收集子集 35 | result.push_back(path); 36 | //终止条件,可以没有,因为后面的for循环也会直接退出 37 | if(stratIndex == nums.size()) 38 | { 39 | return; 40 | } 41 | 42 | for(int i = startIndex; i < nums.size(); i++) 43 | { 44 | //层内去重 45 | if() 46 | continue; 47 | path.push_back(nums[i]); 48 | backTracking(nums,i+1); 49 | path.pop_back(); 50 | } 51 | } 52 | 53 | ``` 54 | 3. 排列问题 55 | ```c++ 56 | void backTracking(vector &nums, vector &used) 57 | { 58 | if (path.size() == nums.size()) 59 | { 60 | result.push_back(path); 61 | return; 62 | } 63 | 64 | for (int i = 0; i < nums.size(); i++) 65 | { 66 | if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false)//判断当前的层的 67 | continue; 68 | 69 | if (used[i])//判断之前的层的 70 | continue; 71 | 72 | used[i] = true; 73 | path.push_back(nums[i]); 74 | backTracking(nums, used); 75 | used[i] = false; 76 | path.pop_back(); 77 | } 78 | } 79 | ``` -------------------------------------------------------------------------------- /算法学习/总结.md: -------------------------------------------------------------------------------- 1 | # 学习过程 2 | 1. 了解时间复杂度、额外空间复杂度、常数时间等一些基本概念 3 | 2. 学习一些基本的排序算法、递归算法思路 4 | 3. 学习数组、链表、哈希表、有序表等数据结构 5 | 4. 开始刷题 6 | 7 | # 自己容易出现的错误 8 | 1. 忘记写函数返回值,`return result;` 9 | 2. 容易出现`=`和`==`符号写串了 10 | 3. 同样逻辑的代码容易出现复制粘贴后,忘记更改具体的值 11 | 12 | # 总结 13 | ## 循环处理思路 14 | 1. 边界条件选择,应该保持一致性。 15 | - `[left,right]`或者`[left,right)` 16 | 2. 计算循环次数or循环终止条件。 17 | - 终止条件,`a < b`或者`a <= b` 18 | - 每次循环起点,一般是关于循环次数n的函数`f(n)` 19 | 3. 计算循环次数时候,考虑特殊情形。 20 | - 一般是奇数偶数情况处理方式不同,`if(n%2==1){}` 21 | 22 | ## 去重操作 23 | ### 数组有序的 24 | ```c++ 25 | vector nums; 26 | sort(nums.begin(),nums.end(),less()); 27 | for(int i = start_index; i < nums.size();i++) 28 | { 29 | // 条件1 保证第一次 以num开始的正常处理 30 | // 条件2 保证后面如果num数字是相等的跳过实现去重 31 | if(i > start_index && nums[i] == nums[i-1]) 32 | continue; 33 | 34 | // 回溯算法 35 | } 36 | ``` 37 | ### 数组无序的 38 | ```c++ 39 | vector nums; 40 | set have; 41 | for(int i = start_index; i < nums.size();i++) 42 | { 43 | if(have.count(nums[i])) 44 | continue; 45 | 46 | // 回溯算法 47 | have.insert(nums[i]); 48 | } 49 | ``` 50 | 51 | ## 递归处理思路 52 | ### 202301 53 | 1. 定义递归函数`fun(x)`,`fun(x)`内部一般过程如下 54 | 1. 判断递归终止条件`return`。 55 | 2. 处理获取新的函数的输入参数`x`,调用函数`fun(x)`。如果右端递归方式处理完`x`调用,就基本上就不用下一步的具体功能实现。 56 | 3. 每一步递归过程中的具体功能实现,也可以理解为递归到最小问题的时候需要处理的操作。处理完成`return` 57 | ### 202304 58 | 1. 定义递归函数`y =fun(x)`,`y`是期望递归函数返回的结果,`x`每次递归的输入已知量 59 | 2. `f(x)`中的具体实现 60 | 1. 判断终止条件,返回`return y` 61 | 2. 处理获得每次递归的输入`x`,递归调用 `y=fun(x)` 62 | 3. 根据递归函数的功能,处理数据交换输出之类的。 63 | 4. 处理完成返回`return y` 64 | 65 | 66 | ## 临时变量 67 | 1. 凡是 `a = fun(x)`的部分,一般需要个临时变量存储**等号左边的变量`a`** 68 | ```c++ 69 | ListNode *temp = cur->next->next->next; 70 | ListNode *temp1 = cur->next->next; 71 | 72 | cur->next->next->next = cur->next; // 2->1 交换 73 | cur->next->next = temp; // 1->3 指向下一个 74 | cur->next = temp1; // cur->next = 2 类似头结点 75 | ``` -------------------------------------------------------------------------------- /面试问题/面试问题.md: -------------------------------------------------------------------------------- 1 | > 记录面试遇到过的一些问题,具体的答案这里不写SLAM或者cpp文件夹里面都会整理,没整理的就是我也不会的,并且没查到的。 2 | > 注意自己简历上写的内容,不会的不要写可能被问。 3 | > 注意岗位的任职需求,面试问题大概率和岗位需求有关系。 4 | # C++相关 5 | ## 一些关键字的用法 6 | - `const` 7 | - `static` 8 | - `final` 9 | ## 智能指针有哪些,具体用法? 10 | - `weak_ptr`具体如何解决指针的循环嵌套 11 | ## 迭代器失效,怎么出现的如何解决? 12 | - 牵扯到STL内存分配策略 13 | ## 模板 14 | - 类型推到时机 15 | - 定义 16 | ## 多态继承相关 17 | - 多态的方式? 18 | - 类继承机制,构造析构顺序,为什么定义虚函数? 19 | ## 内存池 20 | - 碎片内存? 21 | ## 多线程 22 | - 使用过哪些多线程实现机制 23 | ## c++底层 24 | - 问定义的class的字节数 25 | 26 | # 数据结构相关 27 | ## KD-Tree 28 | - 如果构建?如何查询? 29 | 30 | # SLAM以及数学相关 31 | ## 熟悉的开源SLAM算法 32 | ### ORB-SALM 33 | - 特征点均匀化策略 34 | - 特征匹配策略 BOW 匀速模型 极线搜索 35 | - 共视关键帧的选取策略 36 | ### VINS 37 | - 初始化 38 | - IMU预积分 39 | - 边缘化FEJ 40 | 41 | ## 重投影误差。 42 | 43 | ## 单应矩阵和基础矩阵。包括如何求解?具体适用条件的区别? 44 | 45 | ## 优化算法中高斯牛顿与L-M算法的区别? 46 | - 建议从牛顿法怎么过度到高斯牛顿法开始回答 47 | 48 | ## RANSAC算法的具体实现原理?RANSAC算法的迭代次数如何设置? 49 | 50 | ## SVD分解介绍过程 51 | 52 | # 项目相关 53 | ## 简历上写的开源项目的异同差别 54 | ## 项目的代码实现?代码量?使用过哪些c++的特性? 55 | 56 | # HR面or公司相关问题 57 | ## 自我介绍 58 | ## 课题出发点、思路流程、途中遇到的问题,以及如何克服 59 | ## 介绍自己与周围同学相比的优势劣势 60 | ## 如何了解到该公司企业 61 | ## 挑选公司企业的时候的有哪些考虑 62 | ## 整体的应聘进展,其他公司啥的 63 | 64 | # 反问 65 | ## 公司的工作时间 66 | ## 公司具体业务 67 | ## 具体招聘流程时间 68 | ## 培养机制 69 | 70 | 71 | 72 | # 论坛看到的[工作技能建议](https://www.bilibili.com/video/BV1MV4y1J7xW/?spm_id_from=pageDriver&vd_source=d2698384821931f16375af13c5b44a9f) 73 | ## 理论 74 | - 数学 75 | - 矩阵 76 | - 坐标变换 77 | - 优化 78 | - 特征提取 79 | ## 系统 80 | - SLAM框架 81 | - VINS、ORB 82 | ## 计算机/编程 83 | - c++ 84 | - 可视化、调试 85 | - 工程化 --------------------------------------------------------------------------------